Merge "Revert "Explicit registraton to refresh rate"" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 302168d8..834398e 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -824,6 +824,11 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+cc_aconfig_library {
+    name: "android.media.tv.flags-aconfig-cc",
+    aconfig_declarations: "android.media.tv.flags-aconfig",
+}
+
 // Permissions
 aconfig_declarations {
     name: "android.permission.flags-aconfig",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5e0428b..05d6e88 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -29,8 +29,5 @@
 
 ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
 
-# This flag check hook runs only for "packages/SystemUI" subdirectory. If you want to include this check for other subdirectories, please modify flag_check.py.
-flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PROJECT}
-
 [Tool Paths]
 ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index 1e299cd..f16f2ca 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -50,6 +50,7 @@
         "junit-params",
         "core-tests-support",
         "guava",
+        "perfetto_trace_java_protos",
     ],
 
     libs: ["android.test.base.stubs.system"],
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index 0d64c39..bf7c96a 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -17,6 +17,8 @@
 
 package android.os;
 
+import static android.os.PerfettoTrace.Category;
+
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.ShellHelper;
@@ -31,19 +33,35 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig.BufferConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig.DataSource;
+import perfetto.protos.TrackEventConfigOuterClass.TrackEventConfig;
+
 @RunWith(AndroidJUnit4.class)
 public class TracePerfTest {
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
+    private static final String FOO = "foo";
+    private static final Category FOO_CATEGORY = new Category(FOO);
+    private static PerfettoTrace.Session sPerfettoSession;
+
     @BeforeClass
     public static void startTracing() {
         ShellHelper.runShellCommandRaw("atrace -c --async_start -a *");
+        PerfettoTrace.register(false /* isBackendInProcess */);
+        FOO_CATEGORY.register();
+        sPerfettoSession = new PerfettoTrace.Session(false /* isBackendInProcess */,
+                                                      getTraceConfig(FOO).toByteArray());
     }
 
     @AfterClass
     public static void endTracing() {
         ShellHelper.runShellCommandRaw("atrace --async_stop");
+        FOO_CATEGORY.unregister();
+        sPerfettoSession.close();
     }
 
     @Before
@@ -84,4 +102,61 @@
             Trace.setCounter("testCounter", 123);
         }
     }
+
+    @Test
+    public void testInstant() {
+        Trace.instant(Trace.TRACE_TAG_APP, "testInstantA");
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Trace.instant(Trace.TRACE_TAG_APP, "testInstantA");
+        }
+    }
+
+    @Test
+    public void testInstantPerfetto() {
+        PerfettoTrace.instant(FOO_CATEGORY, "testInstantP").emit();
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            PerfettoTrace.instant(FOO_CATEGORY, "testInstantP").emit();
+        }
+    }
+
+    @Test
+    public void testInstantPerfettoWithArgs() {
+        PerfettoTrace.instant(FOO_CATEGORY, "testInstantP")
+                .addArg("foo", "bar")
+                .addFlow(1)
+                .emit();
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            PerfettoTrace.instant(FOO_CATEGORY, "testInstantP")
+                    .addArg("foo", "bar")
+                    .addFlow(1)
+                    .emit();
+        }
+    }
+
+    private static TraceConfig getTraceConfig(String cat) {
+        BufferConfig bufferConfig = BufferConfig.newBuilder().setSizeKb(1024).build();
+        TrackEventConfig trackEventConfig = TrackEventConfig
+                .newBuilder()
+                .addEnabledCategories(cat)
+                .build();
+        DataSourceConfig dsConfig = DataSourceConfig
+                .newBuilder()
+                .setName("track_event")
+                .setTargetBuffer(0)
+                .setTrackEventConfig(trackEventConfig)
+                .build();
+        DataSource ds = DataSource.newBuilder().setConfig(dsConfig).build();
+        TraceConfig traceConfig = TraceConfig
+                .newBuilder()
+                .addBuffers(bufferConfig)
+                .addDataSources(ds)
+                .build();
+        return traceConfig;
+    }
 }
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
index a6e9807..9181ef0 100644
--- a/apex/jobscheduler/service/aconfig/alarm.aconfig
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -7,3 +7,13 @@
     description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due"
     bug: "314907186"
 }
+
+flag {
+    name: "acquire_wakelock_before_send"
+    namespace: "backstage_power"
+    description: "Acquire the userspace alarm wakelock before sending the alarm"
+    bug: "391413964"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 829442a..f89b13d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -5334,6 +5334,18 @@
         public void deliverLocked(Alarm alarm, long nowELAPSED) {
             final long workSourceToken = ThreadLocalWorkSource.setUid(
                     getAlarmAttributionUid(alarm));
+
+            if (Flags.acquireWakelockBeforeSend()) {
+                // Acquire the wakelock before starting the app. This needs to be done to avoid
+                // random stalls in the receiving app in case a suspend attempt is already in
+                // progress. See b/391413964 for an incident where this was found to happen.
+                if (mBroadcastRefCount == 0) {
+                    setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true);
+                    mWakeLock.acquire();
+                    mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1, 0).sendToTarget();
+                }
+            }
+
             try {
                 if (alarm.operation != null) {
                     // PendingIntent alarm
@@ -5399,14 +5411,16 @@
                 ThreadLocalWorkSource.restore(workSourceToken);
             }
 
-            // The alarm is now in flight; now arrange wakelock and stats tracking
             if (DEBUG_WAKELOCK) {
                 Slog.d(TAG, "mBroadcastRefCount -> " + (mBroadcastRefCount + 1));
             }
-            if (mBroadcastRefCount == 0) {
-                setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true);
-                mWakeLock.acquire();
-                mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1, 0).sendToTarget();
+            if (!Flags.acquireWakelockBeforeSend()) {
+                // The alarm is now in flight; now arrange wakelock and stats tracking
+                if (mBroadcastRefCount == 0) {
+                    setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true);
+                    mWakeLock.acquire();
+                    mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1, 0).sendToTarget();
+                }
             }
             final InFlight inflight = new InFlight(AlarmManagerService.this, alarm, nowELAPSED);
             mInFlight.add(inflight);
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 787fdee..3314b4a 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -648,7 +648,7 @@
 
 java_api_library {
     name: "android-non-updatable.stubs.module_lib.from-text",
-    api_surface: "module_lib",
+    api_surface: "module-lib",
     api_contributions: [
         "api-stubs-docs-non-updatable.api.contribution",
         "system-api-stubs-docs-non-updatable.api.contribution",
@@ -668,7 +668,7 @@
 // generated from this module, as this module is strictly used for hiddenapi only.
 java_api_library {
     name: "android-non-updatable.stubs.test_module_lib",
-    api_surface: "module_lib",
+    api_surface: "module-lib",
     api_contributions: [
         "api-stubs-docs-non-updatable.api.contribution",
         "system-api-stubs-docs-non-updatable.api.contribution",
@@ -689,7 +689,7 @@
 
 java_api_library {
     name: "android-non-updatable.stubs.system_server.from-text",
-    api_surface: "system_server",
+    api_surface: "system-server",
     api_contributions: [
         "api-stubs-docs-non-updatable.api.contribution",
         "system-api-stubs-docs-non-updatable.api.contribution",
diff --git a/core/api/current.txt b/core/api/current.txt
index 6964866..2192965 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -452,7 +452,7 @@
     field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
     field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
     field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
-    field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final int adServiceTypes;
+    field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final int adServiceTypes = 16844452; // 0x10106a4
     field public static final int addPrintersActivity = 16843750; // 0x10103e6
     field public static final int addStatesFromChildren = 16842992; // 0x10100f0
     field public static final int adjustViewBounds = 16843038; // 0x101011e
@@ -1017,7 +1017,7 @@
     field public static final int insetRight = 16843192; // 0x10101b8
     field public static final int insetTop = 16843193; // 0x10101b9
     field public static final int installLocation = 16843447; // 0x10102b7
-    field @FlaggedApi("android.security.enable_intent_matching_flags") public static final int intentMatchingFlags;
+    field @FlaggedApi("android.security.enable_intent_matching_flags") public static final int intentMatchingFlags = 16844457; // 0x10106a9
     field public static final int interactiveUiTimeout = 16844181; // 0x1010595
     field public static final int interpolator = 16843073; // 0x1010141
     field public static final int intro = 16844395; // 0x101066b
@@ -1072,7 +1072,7 @@
     field public static final int label = 16842753; // 0x1010001
     field public static final int labelFor = 16843718; // 0x10103c6
     field @Deprecated public static final int labelTextSize = 16843317; // 0x1010235
-    field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int languageSettingsActivity;
+    field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int languageSettingsActivity = 16844453; // 0x10106a5
     field public static final int languageTag = 16844040; // 0x1010508
     field public static final int largeHeap = 16843610; // 0x101035a
     field public static final int largeScreens = 16843398; // 0x1010286
@@ -1085,7 +1085,7 @@
     field public static final int layout = 16842994; // 0x10100f2
     field public static final int layoutAnimation = 16842988; // 0x10100ec
     field public static final int layoutDirection = 16843698; // 0x10103b2
-    field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int layoutLabel;
+    field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int layoutLabel = 16844458; // 0x10106aa
     field public static final int layoutMode = 16843738; // 0x10103da
     field public static final int layout_above = 16843140; // 0x1010184
     field public static final int layout_alignBaseline = 16843142; // 0x1010186
@@ -1207,7 +1207,7 @@
     field public static final int minResizeHeight = 16843670; // 0x1010396
     field public static final int minResizeWidth = 16843669; // 0x1010395
     field public static final int minSdkVersion = 16843276; // 0x101020c
-    field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int minSdkVersionFull;
+    field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int minSdkVersionFull = 16844461; // 0x10106ad
     field public static final int minWidth = 16843071; // 0x101013f
     field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
     field public static final int minimumVerticalAngle = 16843902; // 0x101047e
@@ -1282,7 +1282,7 @@
     field public static final int paddingStart = 16843699; // 0x10103b3
     field public static final int paddingTop = 16842967; // 0x10100d7
     field public static final int paddingVertical = 16844094; // 0x101053e
-    field @FlaggedApi("android.content.pm.app_compat_option_16kb") public static final int pageSizeCompat;
+    field @FlaggedApi("android.content.pm.app_compat_option_16kb") public static final int pageSizeCompat = 16844459; // 0x10106ab
     field public static final int panelBackground = 16842846; // 0x101005e
     field public static final int panelColorBackground = 16842849; // 0x1010061
     field public static final int panelColorForeground = 16842848; // 0x1010060
@@ -1624,7 +1624,7 @@
     field public static final int summaryColumn = 16843426; // 0x10102a2
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
-    field @FlaggedApi("android.view.accessibility.supplemental_description") public static final int supplementalDescription;
+    field @FlaggedApi("android.view.accessibility.supplemental_description") public static final int supplementalDescription = 16844456; // 0x10106a8
     field public static final int supportedTypes = 16844369; // 0x1010651
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsBatteryGameMode = 16844374; // 0x1010656
@@ -1871,7 +1871,7 @@
     field public static final int wallpaperIntraOpenExitAnimation = 16843416; // 0x1010298
     field public static final int wallpaperOpenEnterAnimation = 16843411; // 0x1010293
     field public static final int wallpaperOpenExitAnimation = 16843412; // 0x1010294
-    field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final int wantsRoleHolderPriority;
+    field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final int wantsRoleHolderPriority = 16844460; // 0x10106ac
     field public static final int webTextViewStyle = 16843449; // 0x10102b9
     field public static final int webViewStyle = 16842885; // 0x1010085
     field public static final int weekDayTextAppearance = 16843592; // 0x1010348
@@ -8893,8 +8893,8 @@
   }
 
   @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager {
-    method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
-    method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
+    method @RequiresPermission(value=android.Manifest.permission.EXECUTE_APP_FUNCTIONS, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
+    method @RequiresPermission(value=android.Manifest.permission.EXECUTE_APP_FUNCTIONS, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
     method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
     method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
     field public static final int APP_FUNCTION_STATE_DEFAULT = 0; // 0x0
@@ -11134,6 +11134,7 @@
     field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
     field public static final String TELEPHONY_SERVICE = "phone";
     field public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
+    field public static final String TETHERING_SERVICE = "tethering";
     field public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
     field public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
     field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final String TV_AD_SERVICE = "tv_ad";
@@ -19345,7 +19346,7 @@
   public class BiometricManager {
     method @Deprecated @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate();
     method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate(int);
-    method @FlaggedApi("android.hardware.biometrics.last_authentication_time") @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public long getLastAuthenticationTime(int);
+    method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public long getLastAuthenticationTime(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public android.hardware.biometrics.BiometricManager.Strings getStrings(int);
     field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
     field @FlaggedApi("android.hardware.biometrics.identity_check_api") public static final int BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE = 20; // 0x14
@@ -19353,7 +19354,7 @@
     field @FlaggedApi("android.hardware.biometrics.identity_check_api") public static final int BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS = 21; // 0x15
     field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
     field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
-    field @FlaggedApi("android.hardware.biometrics.last_authentication_time") public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
+    field public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
     field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
   }
 
@@ -19406,7 +19407,7 @@
     field public static final int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
     field public static final int BIOMETRIC_ERROR_USER_CANCELED = 10; // 0xa
     field public static final int BIOMETRIC_ERROR_VENDOR = 8; // 0x8
-    field @FlaggedApi("android.hardware.biometrics.last_authentication_time") public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
+    field public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
   }
 
   public abstract static class BiometricPrompt.AuthenticationCallback {
@@ -20784,6 +20785,7 @@
     method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
     method @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public void registerDisplayListener(@NonNull java.util.concurrent.Executor, long, @NonNull android.hardware.display.DisplayManager.DisplayListener);
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
+    field @FlaggedApi("com.android.server.display.feature.flags.display_category_built_in") public static final String DISPLAY_CATEGORY_BUILT_IN_DISPLAYS = "android.hardware.display.category.BUILT_IN_DISPLAYS";
     field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
     field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_TYPE_DISPLAY_ADDED = 1L; // 0x1L
     field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_TYPE_DISPLAY_CHANGED = 4L; // 0x4L
@@ -21749,6 +21751,7 @@
     field public static final int CHANNEL_IN_X_AXIS = 2048; // 0x800
     field public static final int CHANNEL_IN_Y_AXIS = 4096; // 0x1000
     field public static final int CHANNEL_IN_Z_AXIS = 8192; // 0x2000
+    field @FlaggedApi("android.media.audio.sony_360ra_mpegh_3d_format") public static final int CHANNEL_OUT_13POINT0 = 30136348; // 0x1cbd81c
     field public static final int CHANNEL_OUT_5POINT1 = 252; // 0xfc
     field public static final int CHANNEL_OUT_5POINT1POINT2 = 3145980; // 0x3000fc
     field public static final int CHANNEL_OUT_5POINT1POINT4 = 737532; // 0xb40fc
@@ -42267,6 +42270,7 @@
     method public int getRank();
     method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
     method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies();
+    method @FlaggedApi("android.app.nm_summarization") @Nullable public String getSummarization();
     method public int getSuppressedVisualEffects();
     method public int getUserSentiment();
     method public boolean isAmbient();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 937a9ff..ab82411 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -150,7 +150,6 @@
     field @FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission") public static final String EMBED_ANY_APP_IN_UNTRUSTED_MODE = "android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE";
     field @FlaggedApi("android.content.pm.emergency_install_permission") public static final String EMERGENCY_INSTALL_PACKAGES = "android.permission.EMERGENCY_INSTALL_PACKAGES";
     field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
-    field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS_TRUSTED = "android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED";
     field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS";
     field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
@@ -464,7 +463,7 @@
 
   public static final class R.attr {
     field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
-    field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final int backgroundPermission;
+    field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final int backgroundPermission = 16844455; // 0x10106a7
     field @FlaggedApi("android.content.res.manifest_flagging") public static final int featureFlag = 16844428; // 0x101068c
     field public static final int gameSessionService = 16844373; // 0x1010655
     field public static final int hotwordDetectionService = 16844326; // 0x1010626
@@ -543,7 +542,7 @@
     field public static final int config_systemCallStreaming = 17039431; // 0x1040047
     field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039
     field public static final int config_systemContacts = 17039403; // 0x104002b
-    field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final int config_systemDependencyInstaller;
+    field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final int config_systemDependencyInstaller = 17039434; // 0x104004a
     field public static final int config_systemFinancedDeviceController = 17039430; // 0x1040046
     field public static final int config_systemGallery = 17039399; // 0x1040027
     field public static final int config_systemNotificationIntelligence = 17039413; // 0x1040035
@@ -555,7 +554,7 @@
     field public static final int config_systemTextIntelligence = 17039414; // 0x1040036
     field public static final int config_systemUi = 17039418; // 0x104003a
     field public static final int config_systemUiIntelligence = 17039410; // 0x1040032
-    field @FlaggedApi("android.permission.flags.system_vendor_intelligence_role_enabled") public static final int config_systemVendorIntelligence;
+    field @FlaggedApi("android.permission.flags.system_vendor_intelligence_role_enabled") public static final int config_systemVendorIntelligence = 17039435; // 0x104004b
     field public static final int config_systemVisualIntelligence = 17039415; // 0x1040037
     field public static final int config_systemWearHealthService = 17039428; // 0x1040044
     field public static final int config_systemWellbeing = 17039408; // 0x1040030
@@ -3800,7 +3799,6 @@
     field public static final String STATS_MANAGER = "stats";
     field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
     field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
-    field public static final String TETHERING_SERVICE = "tethering";
     field @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") public static final String THREAD_NETWORK_SERVICE = "thread_network";
     field public static final String TIME_MANAGER_SERVICE = "time_manager";
     field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
@@ -13446,6 +13444,7 @@
     field public static final String KEY_RANKING_SCORE = "key_ranking_score";
     field public static final String KEY_SENSITIVE_CONTENT = "key_sensitive_content";
     field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+    field @FlaggedApi("android.app.nm_summarization") public static final String KEY_SUMMARIZATION = "key_summarization";
     field public static final String KEY_TEXT_REPLIES = "key_text_replies";
     field @FlaggedApi("android.service.notification.notification_classification") public static final String KEY_TYPE = "key_type";
     field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
@@ -13480,6 +13479,7 @@
     method public final void unsnoozeNotification(@NonNull String);
     field public static final String ACTION_NOTIFICATION_ASSISTANT_DETAIL_SETTINGS = "android.service.notification.action.NOTIFICATION_ASSISTANT_DETAIL_SETTINGS";
     field @FlaggedApi("android.service.notification.notification_classification") public static final String ACTION_NOTIFICATION_ASSISTANT_FEEDBACK_SETTINGS = "android.service.notification.action.NOTIFICATION_ASSISTANT_FEEDBACK_SETTINGS";
+    field @FlaggedApi("android.app.nm_summarization") public static final String EXTRA_NOTIFICATION_ADJUSTMENT = "android.service.notification.extra.NOTIFICATION_ADJUSTMENT";
     field @FlaggedApi("android.service.notification.notification_classification") public static final String EXTRA_NOTIFICATION_KEY = "android.service.notification.extra.NOTIFICATION_KEY";
     field public static final String FEEDBACK_RATING = "feedback.rating";
     field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
@@ -16041,7 +16041,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
-    method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCellularIdentifierDisclosureNotificationsEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCellularIdentifierDisclosureNotificationsEnabled();
     method public boolean isDataConnectivityPossible();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
     method @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDomainSelectionSupported();
@@ -16092,7 +16092,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
-    method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableCellularIdentifierDisclosureNotifications(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableCellularIdentifierDisclosureNotifications(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f06bd48..0b0738e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -478,8 +478,8 @@
     method public void destroy();
     method @NonNull public java.util.Set<java.lang.String> getAdoptedShellPermissions();
     method @Deprecated public boolean grantRuntimePermission(String, String, android.os.UserHandle);
-    method public boolean injectInputEvent(@NonNull android.view.InputEvent, boolean, boolean);
-    method public void injectInputEventToInputFilter(@NonNull android.view.InputEvent);
+    method @Deprecated @FlaggedApi("com.android.input.flags.deprecate_uiautomation_input_injection") public boolean injectInputEvent(@NonNull android.view.InputEvent, boolean, boolean);
+    method @Deprecated @FlaggedApi("com.android.input.flags.deprecate_uiautomation_input_injection") public void injectInputEventToInputFilter(@NonNull android.view.InputEvent);
     method public boolean isNodeInCache(@NonNull android.view.accessibility.AccessibilityNodeInfo);
     method public void removeOverridePermissionState(int, @NonNull String);
     method @Deprecated public boolean revokeRuntimePermission(String, String, android.os.UserHandle);
@@ -909,6 +909,14 @@
     method @NonNull public android.companion.AssociationInfo.Builder setTimeApproved(long);
   }
 
+  public final class AssociationRequest implements android.os.Parcelable {
+    method public boolean isSkipRoleGrant();
+  }
+
+  public static final class AssociationRequest.Builder {
+    method @NonNull @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES) public android.companion.AssociationRequest.Builder setSkipRoleGrant(boolean);
+  }
+
   public final class CompanionDeviceManager {
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void enableSecureTransport(boolean);
   }
@@ -1634,6 +1642,10 @@
     method public void setColorSpace(@NonNull android.graphics.ColorSpace.Named);
   }
 
+  @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public final class SharedSessionConfiguration {
+    ctor public SharedSessionConfiguration(int, @NonNull long[]);
+  }
+
 }
 
 package android.hardware.devicestate {
@@ -1723,6 +1735,7 @@
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
+    field public static final String DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED = "android.hardware.display.category.ALL_INCLUDING_DISABLED";
     field public static final String DISPLAY_CATEGORY_REAR = "android.hardware.display.category.REAR";
     field public static final String HDR_OUTPUT_CONTROL_FLAG = "enable_hdr_output_control";
     field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
@@ -2136,6 +2149,7 @@
     method public void setParameterListener(android.media.audiofx.AudioEffect.OnParameterChangeListener);
     method public static byte[] shortToByteArray(short);
     field public static final java.util.UUID EFFECT_TYPE_NULL;
+    field @NonNull public static final java.util.UUID EFFECT_TYPE_SPATIALIZER;
   }
 
   public static class AudioEffect.Descriptor {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 1e97d4f..1ad247e 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -87,7 +87,7 @@
         release_package_messagequeue_implementation: {
             srcs: ["android/os/%s"],
             conditions_default: {
-                srcs: ["android/os/LegacyMessageQueue/MessageQueue.java"],
+                srcs: ["android/os/CombinedMessageQueue/MessageQueue.java"],
             },
         },
     },
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 16dcf2a..8d20e46 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -25,6 +25,7 @@
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -483,6 +484,19 @@
     }
 
     /**
+     * @return Whether the app could be universal resizeable (assuming it's on a large screen and
+     * ignoring possible overrides)
+     * @hide
+     */
+    public boolean canBeUniversalResizeable(@NonNull ApplicationInfo appInfo) {
+        try {
+            return getService().canBeUniversalResizeable(appInfo);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Detaches the navigation bar from the app it was attached to during a transition.
      * @hide
      */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7b9ec4a..2d7ed46 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3959,10 +3959,20 @@
 
     /** Converts a process state to a VM process state. */
     private static int toVmProcessState(int processState) {
-        final int state = ActivityManager.isProcStateJankPerceptible(processState)
-                ? VM_PROCESS_STATE_JANK_PERCEPTIBLE
-                : VM_PROCESS_STATE_JANK_IMPERCEPTIBLE;
-        return state;
+        if (ActivityManager.isProcStateJankPerceptible(processState)) {
+            return VM_PROCESS_STATE_JANK_PERCEPTIBLE;
+        }
+
+        if (Flags.jankPerceptibleNarrow()) {
+            // Unlike other persistent processes, system server is often on
+            // the critical path for application startup. Mark it explicitly
+            // as jank perceptible regardless of processState.
+            if (isSystem()) {
+                return VM_PROCESS_STATE_JANK_PERCEPTIBLE;
+            }
+        }
+
+        return VM_PROCESS_STATE_JANK_IMPERCEPTIBLE;
     }
 
     /** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index c6f62a2..4b1afa5 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -158,6 +158,12 @@
     void reportAssistContextExtras(in IBinder assistToken, in Bundle extras,
             in AssistStructure structure, in AssistContent content, in Uri referrer);
 
+    /**
+     * @return whether the app could be universal resizeable (assuming it's on a large screen and
+     * ignoring possible overrides)
+     */
+    boolean canBeUniversalResizeable(in ApplicationInfo appInfo);
+
     void setFocusedRootTask(int taskId);
     ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo();
     Rect getTaskBounds(int taskId);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 8fa2362..3fb0822 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -114,7 +114,6 @@
     NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, String conversationId, boolean includeDeleted);
     void deleteNotificationChannel(String pkg, String channelId);
     ParceledListSlice getNotificationChannels(String callingPkg, String targetPkg, int userId);
-    ParceledListSlice getOrCreateNotificationChannels(String callingPkg, String targetPkg, int userId, boolean createPrefsIfNeeded);
     ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
     int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
     int getDeletedChannelCount(String pkg, int uid);
@@ -271,8 +270,8 @@
 
     int[] getAllowedAdjustmentKeyTypes();
     void setAssistantAdjustmentKeyTypeState(int type, boolean enabled);
-    String[] getTypeAdjustmentDeniedPackages();
-    void setTypeAdjustmentForPackageState(String pkg, boolean enabled);
+    boolean isAdjustmentSupportedForPackage(String key, String pkg);
+    void setAdjustmentSupportedForPackage(String key, String pkg, boolean enabled);
 
     // TODO: b/389918945 - Remove once nm_binder_perf flags are going to Nextfood.
     void incrementCounter(String metricId);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fcb817e..ba49149 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1772,6 +1772,11 @@
      */
     public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
 
+    /**
+     * @hide
+     */
+    public static final String EXTRA_SUMMARIZED_CONTENT = "android.summarization";
+
     @UnsupportedAppUsage
     private Icon mSmallIcon;
     @UnsupportedAppUsage
@@ -4393,6 +4398,9 @@
      */
     @Nullable
     public Pair<RemoteInput, Action> findRemoteInputActionPair(boolean requiresFreeform) {
+        if (isPromotedOngoing()) {
+            return null;
+        }
         if (actions == null) {
             return null;
         }
@@ -4418,8 +4426,7 @@
      * notification) out of the actions in this notification.
      */
     public @NonNull List<Notification.Action> getContextualActions() {
-        if (actions == null) return Collections.emptyList();
-
+        if (actions == null || isPromotedOngoing()) return Collections.emptyList();
         List<Notification.Action> contextualActions = new ArrayList<>();
         for (Notification.Action action : actions) {
             if (action.isContextual()) {
@@ -6454,6 +6461,11 @@
             if (mActions == null) return Collections.emptyList();
             List<Notification.Action> standardActions = new ArrayList<>();
             for (Notification.Action action : mActions) {
+                // Actions with RemoteInput are ignored for RONs.
+                if (mN.isPromotedOngoing()
+                        && hasValidRemoteInput(action)) {
+                    continue;
+                }
                 if (!action.isContextual()) {
                     standardActions.add(action);
                 }
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 73d26b8..c6f008c 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,10 @@
  */
 package android.app;
 
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
 import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT;
 
 import android.annotation.FlaggedApi;
@@ -37,6 +41,7 @@
 import android.os.vibrator.persistence.VibrationXmlParser;
 import android.os.vibrator.persistence.VibrationXmlSerializer;
 import android.provider.Settings;
+import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.text.TextUtils;
 import android.util.Log;
@@ -1606,6 +1611,26 @@
         return sb.toString();
     }
 
+    /**
+     * Get the reserved bundle channel ID for an Adjustment type
+     * @param the Adjustment type
+     * @return the channel ID, or null if type is invalid
+     * @hide
+     */
+    public static @Nullable String getChannelIdForBundleType(@Adjustment.Types int type) {
+        switch (type) {
+            case TYPE_CONTENT_RECOMMENDATION:
+                return RECS_ID;
+            case TYPE_NEWS:
+                return NEWS_ID;
+            case TYPE_PROMOTION:
+                return PROMOTIONS_ID;
+            case TYPE_SOCIAL_MEDIA:
+                return SOCIAL_MEDIA_ID;
+        }
+        return null;
+    }
+
     public static final @android.annotation.NonNull Creator<NotificationChannel> CREATOR =
             new Creator<NotificationChannel>() {
         @Override
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 1e1ec60..6e31779 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1264,8 +1264,7 @@
                     mNotificationChannelListCache.query(new NotificationChannelQuery(
                             mContext.getOpPackageName(),
                             mContext.getPackageName(),
-                            mContext.getUserId(),
-                            true)));  // create (default channel) if needed
+                            mContext.getUserId())));
         } else {
             INotificationManager service = service();
             try {
@@ -1293,8 +1292,7 @@
                     mNotificationChannelListCache.query(new NotificationChannelQuery(
                             mContext.getOpPackageName(),
                             mContext.getPackageName(),
-                            mContext.getUserId(),
-                            true)));  // create (default channel) if needed
+                            mContext.getUserId())));
         } else {
             INotificationManager service = service();
             try {
@@ -1320,8 +1318,7 @@
             return mNotificationChannelListCache.query(new NotificationChannelQuery(
                     mContext.getOpPackageName(),
                     mContext.getPackageName(),
-                    mContext.getUserId(),
-                    false));
+                    mContext.getUserId()));
         } else {
             INotificationManager service = service();
             try {
@@ -1461,8 +1458,8 @@
                 public List<NotificationChannel> apply(NotificationChannelQuery query) {
                     INotificationManager service = service();
                     try {
-                        return service.getOrCreateNotificationChannels(query.callingPkg,
-                                query.targetPkg, query.userId, query.createIfNeeded).getList();
+                        return service.getNotificationChannels(query.callingPkg,
+                                query.targetPkg, query.userId).getList();
                     } catch (RemoteException e) {
                         throw e.rethrowFromSystemServer();
                     }
@@ -1490,8 +1487,7 @@
     private record NotificationChannelQuery(
             String callingPkg,
             String targetPkg,
-            int userId,
-            boolean createIfNeeded) {}
+            int userId) {}
 
     /**
      * @hide
@@ -2175,19 +2171,6 @@
     /**
      * @hide
      */
-    @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-    public void setTypeAdjustmentForPackageState(@NonNull String pkg, boolean enabled) {
-        INotificationManager service = service();
-        try {
-            service.setTypeAdjustmentForPackageState(pkg, enabled);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * @hide
-     */
     public List<String> getEnabledNotificationListenerPackages() {
         INotificationManager service = service();
         try {
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 660d880..e9b2ffc 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -2321,12 +2321,14 @@
         @GuardedBy("mLock")
         private int mBlockHash = 0;
 
-        // The number of nonces that the native layer can hold.  This is maintained for debug and
-        // logging.
-        private final int mMaxNonce;
+        // The number of nonces that the native layer can hold.  This is maintained for debug,
+        // logging, and testing.
+        @VisibleForTesting
+        public final int mMaxNonce;
 
         // The size of the native byte block.
-        private final int mMaxByte;
+        @VisibleForTesting
+        public final int mMaxByte;
 
         /** @hide */
         @VisibleForTesting
@@ -2483,18 +2485,20 @@
             }
         }
 
-        static final AtomicLong sStoreCount = new AtomicLong();
-
-
         // Add a string to the local copy of the block and write the block to shared memory.
         // Return the index of the new string.  If the string has already been recorded, the
-        // shared memory is not updated but the index of the existing string is returned.
+        // shared memory is not updated but the index of the existing string is returned.  Only
+        // mMaxNonce strings can be stored; if mMaxNonce strings have already been allocated,
+        // the method throws.
         public int storeName(@NonNull String str) {
             synchronized (mLock) {
                 Integer handle = mStringHandle.get(str);
                 if (handle == null) {
                     throwIfImmutable();
                     throwIfBadString(str);
+                    if (mHighestIndex + 1 >= mMaxNonce) {
+                        throw new RuntimeException("nonce limit exceeded");
+                    }
                     byte[] block = new byte[mMaxByte];
                     nativeGetByteBlock(mPtr, 0, block);
                     appendStringToMapLocked(str, block);
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index b7285c3..01868cc6 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -60,6 +60,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -195,12 +196,40 @@
      */
     private static final int DEFAULT_SIM_LOCKED_DISABLED_FLAGS = DISABLE_EXPAND;
 
-    /** @hide */
-    public static final int NAVIGATION_HINT_BACK_ALT      = 1 << 0;
-    /** @hide */
-    public static final int NAVIGATION_HINT_IME_SHOWN     = 1 << 1;
-    /** @hide */
-    public static final int NAVIGATION_HINT_IME_SWITCHER_SHOWN = 1 << 2;
+    /**
+     * The back button is visually adjusted to indicate that it will dismiss the IME when pressed.
+     * This only takes effect while the IME is visible. By default, it is set while the IME is
+     * visible, but may be overridden by the
+     * {@link android.inputmethodservice.InputMethodService.BackDispositionMode backDispositionMode}
+     * set by the IME.
+     *
+     * @hide
+     */
+    public static final int NAVBAR_BACK_DISMISS_IME = 1 << 0;
+    /**
+     * The IME is visible.
+     *
+     * @hide
+     */
+    public static final int NAVBAR_IME_VISIBLE = 1 << 1;
+    /**
+     * The IME Switcher button is visible. This only takes effect while the IME is visible.
+     *
+     * @hide
+     */
+    public static final int NAVBAR_IME_SWITCHER_BUTTON_VISIBLE = 1 << 2;
+    /**
+     * Navigation bar state flags.
+     *
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "NAVBAR_" }, value = {
+            NAVBAR_BACK_DISMISS_IME,
+            NAVBAR_IME_VISIBLE,
+            NAVBAR_IME_SWITCHER_BUTTON_VISIBLE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NavbarFlags {}
 
     /** @hide */
     public static final int WINDOW_STATUS_BAR = 1;
@@ -1325,6 +1354,22 @@
     }
 
     /** @hide */
+    @NonNull
+    public static String navbarFlagsToString(@NavbarFlags int flags) {
+        final var flagStrings = new ArrayList<String>();
+        if ((flags & NAVBAR_BACK_DISMISS_IME) != 0) {
+            flagStrings.add("NAVBAR_BACK_DISMISS_IME");
+        }
+        if ((flags & NAVBAR_IME_VISIBLE) != 0) {
+            flagStrings.add("NAVBAR_IME_VISIBLE");
+        }
+        if ((flags & NAVBAR_IME_SWITCHER_BUTTON_VISIBLE) != 0) {
+            flagStrings.add("NAVBAR_IME_SWITCHER_BUTTON_VISIBLE");
+        }
+        return String.join(" | ", flagStrings);
+    }
+
+    /** @hide */
     public static String windowStateToString(int state) {
         if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
         if (state == WINDOW_STATE_HIDDEN) return "WINDOW_STATE_HIDDEN";
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 6f8e335..ba8fbc1 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -18,6 +18,8 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.input.flags.Flags.FLAG_DEPRECATE_UIAUTOMATION_INPUT_INJECTION;
+
 import android.accessibilityservice.AccessibilityGestureEvent;
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityService.Callbacks;
@@ -26,6 +28,7 @@
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.accessibilityservice.MagnificationConfig;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -108,7 +111,10 @@
  * client should be using a higher-level library or implement high-level functions.
  * For example, performing a tap on the screen requires construction and injecting
  * of a touch down and up events which have to be delivered to the system by a
- * call to {@link #injectInputEvent(InputEvent, boolean)}.
+ * call to {@link #injectInputEvent(InputEvent, boolean)}. <strong>Note:</strong> For CTS tests, it
+ * is preferable to inject input events using uinput (com.android.cts.input.UinputDevice) or hid
+ * devices (com.android.cts.input.HidDevice). Alternatively, use InjectInputInProcess
+ * (com.android.cts.input.InjectInputInProcess) for in-process injection.
  * </p>
  * <p>
  * The APIs exposed by this class operate across applications enabling a client
@@ -122,7 +128,7 @@
     private static final String LOG_TAG = UiAutomation.class.getSimpleName();
 
     private static final boolean DEBUG = false;
-    private static final boolean VERBOSE = false;
+    private static final boolean VERBOSE = Build.IS_DEBUGGABLE;
 
     private static final int CONNECTION_ID_UNDEFINED = -1;
 
@@ -222,7 +228,8 @@
 
     private OnAccessibilityEventListener mOnAccessibilityEventListener;
 
-    private boolean mWaitingForEventDelivery;
+    // Count the nested clients waiting for data delivery
+    private int mCurrentEventWatchersCount = 0;
 
     private long mLastEventTimeMillis;
 
@@ -956,9 +963,17 @@
      * <strong>Note:</strong> It is caller's responsibility to recycle the event.
      * </p>
      *
-     * @param event The event to inject.
-     * @param sync Whether to inject the event synchronously.
-     * @return Whether event injection succeeded.
+     * <p>
+     * <strong>Note:</strong> Avoid this method when injecting input events in CTS tests. Instead
+     * use uinput (com.android.cts.input.UinputDevice)
+     * or hid devices (com.android.cts.input.HidDevice), as they provide a more accurate simulation
+     * of real device behavior. Alternatively, InjectInputInProcess
+     * (com.android.cts.input.InjectInputProcess) can be used for in-process injection.
+     * </p>
+     *
+     * @param event the event to inject
+     * @param sync whether to inject the event synchronously
+     * @return {@code true} if event injection succeeded
      */
     public boolean injectInputEvent(InputEvent event, boolean sync) {
         return injectInputEvent(event, sync, true /* waitForAnimations */);
@@ -971,15 +986,21 @@
      * <strong>Note:</strong> It is caller's responsibility to recycle the event.
      * </p>
      *
-     * @param event The event to inject.
-     * @param sync  Whether to inject the event synchronously.
-     * @param waitForAnimations Whether to wait for all window container animations and surface
-     *   operations to complete.
-     * @return Whether event injection succeeded.
+     * @param event the event to inject
+     * @param sync  whether to inject the event synchronously.
+     * @param waitForAnimations whether to wait for all window container animations and surface
+     *   operations to complete
+     * @return {@code true} if event injection succeeded
      *
+     * @deprecated for CTS tests prefer inject input events using uinput
+     *   (com.android.cts.input.UinputDevice) or hid devices (com.android.cts.input.HidDevice).
+     *   Alternatively, InjectInputInProcess (com.android.cts.input.InjectInputProcess) can be used
+     *   for in-process injection.
      * @hide
      */
     @TestApi
+    @Deprecated  // Deprecated for CTS tests
+    @FlaggedApi(FLAG_DEPRECATE_UIAUTOMATION_INPUT_INJECTION)
     public boolean injectInputEvent(@NonNull InputEvent event, boolean sync,
             boolean waitForAnimations) {
         try {
@@ -1002,9 +1023,15 @@
      * Events injected to the input subsystem using the standard {@link #injectInputEvent} method
      * skip the accessibility input filter to avoid feedback loops.
      *
+     * @deprecated for CTS tests prefer inject input events using uinput
+     *   (com.android.cts.input.UinputDevice) or hid devices (com.android.cts.input.HidDevice).
+     *   Alternatively, InjectInputInProcess (com.android.cts.input.InjectInputProcess) can be used
+     *   for in-process injection.
      * @hide
      */
     @TestApi
+    @Deprecated
+    @FlaggedApi(FLAG_DEPRECATE_UIAUTOMATION_INPUT_INJECTION)
     public void injectInputEventToInputFilter(@NonNull InputEvent event) {
         try {
             mUiAutomationConnection.injectInputEventToInputFilter(event);
@@ -1132,75 +1159,75 @@
      */
     public AccessibilityEvent executeAndWaitForEvent(Runnable command,
             AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException {
+        int watchersDepth;
+        // Track events added after the index for this command, it is to support nested calls.
+        // This doesn't support concurrent calls correctly.
+        int eventQueueStartIndex;
+        final long executionStartTimeMillis;
+
         // Acquire the lock and prepare for receiving events.
         synchronized (mLock) {
             throwIfNotConnectedLocked();
-            mEventQueue.clear();
-            // Prepare to wait for an event.
-            mWaitingForEventDelivery = true;
+            watchersDepth = ++mCurrentEventWatchersCount;
+            executionStartTimeMillis = SystemClock.uptimeMillis();
+            eventQueueStartIndex = mEventQueue.size();
+        }
+        if (VERBOSE) {
+            Log.v(LOG_TAG, "executeAndWaitForEvent starts at depth=" + watchersDepth + ", "
+                    + "command=" + command + ", filter=" + filter + ", timeout=" + timeoutMillis);
         }
 
-        // Note: We have to release the lock since calling out with this lock held
-        // can bite. We will correctly filter out events from other interactions,
-        // so starting to collect events before running the action is just fine.
-
-        // We will ignore events from previous interactions.
-        final long executionStartTimeMillis = SystemClock.uptimeMillis();
-        // Execute the command *without* the lock being held.
-        command.run();
-
-        List<AccessibilityEvent> receivedEvents = new ArrayList<>();
-
-        // Acquire the lock and wait for the event.
         try {
-            // Wait for the event.
+            // Execute the command *without* the lock being held.
+            command.run();
+            synchronized (mLock) {
+                if (watchersDepth != mCurrentEventWatchersCount) {
+                    throw new IllegalStateException("Unexpected event watchers count, expected: "
+                            + watchersDepth + ", actual: " + mCurrentEventWatchersCount);
+                }
+            }
             final long startTimeMillis = SystemClock.uptimeMillis();
-            while (true) {
-                List<AccessibilityEvent> localEvents = new ArrayList<>();
+            List<AccessibilityEvent> receivedEvents = new ArrayList<>();
+            long elapsedTimeMillis = 0;
+            int currentQueueSize = 0;
+            while (timeoutMillis > elapsedTimeMillis) {
+                AccessibilityEvent event = null;
                 synchronized (mLock) {
-                    localEvents.addAll(mEventQueue);
-                    mEventQueue.clear();
-                }
-                // Drain the event queue
-                while (!localEvents.isEmpty()) {
-                    AccessibilityEvent event = localEvents.remove(0);
-                    // Ignore events from previous interactions.
-                    if (event.getEventTime() < executionStartTimeMillis) {
-                        continue;
-                    }
-                    if (filter.accept(event)) {
-                        return event;
-                    }
-                    receivedEvents.add(event);
-                }
-                // Check if timed out and if not wait.
-                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
-                final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
-                if (remainingTimeMillis <= 0) {
-                    throw new TimeoutException("Expected event not received within: "
-                            + timeoutMillis + " ms among: " + receivedEvents);
-                }
-                synchronized (mLock) {
-                    if (mEventQueue.isEmpty()) {
+                    currentQueueSize = mEventQueue.size();
+                    if (eventQueueStartIndex < currentQueueSize) {
+                        event = mEventQueue.get(eventQueueStartIndex++);
+                    } else {
                         try {
-                            mLock.wait(remainingTimeMillis);
+                            mLock.wait(timeoutMillis - elapsedTimeMillis);
                         } catch (InterruptedException ie) {
                             /* ignore */
                         }
                     }
                 }
+                elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                if (event == null || event.getEventTime() < executionStartTimeMillis) {
+                    continue;
+                }
+                if (filter.accept(event)) {
+                    return event;
+                }
+                receivedEvents.add(event);
             }
+            if (eventQueueStartIndex < currentQueueSize) {
+                Log.w(LOG_TAG, "Timed out before reading all events from the queue");
+            }
+            throw new TimeoutException("Expected event not received before timeout, events: "
+                    + receivedEvents);
         } finally {
-            int size = receivedEvents.size();
-            for (int i = 0; i < size; i++) {
-                receivedEvents.get(i).recycle();
-            }
-
             synchronized (mLock) {
-                mWaitingForEventDelivery = false;
-                mEventQueue.clear();
+                if (--mCurrentEventWatchersCount == 0) {
+                    mEventQueue.clear();
+                }
                 mLock.notifyAll();
             }
+            if (VERBOSE) {
+                Log.v(LOG_TAG, "executeAndWaitForEvent ends at depth=" + watchersDepth);
+            }
         }
     }
 
@@ -1957,7 +1984,7 @@
                         // It is not guaranteed that the accessibility framework sends events by the
                         // order of event timestamp.
                         mLastEventTimeMillis = Math.max(mLastEventTimeMillis, event.getEventTime());
-                        if (mWaitingForEventDelivery) {
+                        if (mCurrentEventWatchersCount > 0) {
                             mEventQueue.add(AccessibilityEvent.obtain(event));
                         }
                         mLock.notifyAll();
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 6fd8db9..0a3891f 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -72,10 +72,10 @@
  * <p>To execute an app function, the caller app can retrieve the {@code functionIdentifier} from
  * the {@code AppFunctionStaticMetadata} document and use it to build an {@link
  * ExecuteAppFunctionRequest}. Then, invoke {@link #executeAppFunction} with the request to execute
- * the app function. Callers need the {@code android.permission.EXECUTE_APP_FUNCTIONS} or {@code
- * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} permission to execute app functions from other
- * apps. An app can always execute its own app functions and doesn't need these permissions.
- * AppFunction SDK provides a convenient way to achieve this and is the preferred method.
+ * the app function. Callers need the {@code android.permission.EXECUTE_APP_FUNCTIONS} permission to
+ * execute app functions from other apps. An app can always execute its own app functions and
+ * doesn't need these permissions. AppFunction SDK provides a convenient way to achieve this and
+ * is the preferred method.
  *
  * <h3>Example</h3>
  *
@@ -141,32 +141,24 @@
      * Executes the app function.
      *
      * <p>Note: Applications can execute functions they define. To execute functions defined in
-     * another component, apps would need to have {@code
-     * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
-     * android.permission.EXECUTE_APP_FUNCTIONS}.
+     * another component, apps would need to have the permission
+     * {@code android.permission.EXECUTE_APP_FUNCTIONS}.
      *
      * @param request the request to execute the app function
      * @param executor the executor to run the callback
      * @param cancellationSignal the cancellation signal to cancel the execution.
      * @param callback the callback to receive the function execution result or error.
      *     <p>If the calling app does not own the app function or does not have {@code
-     *     android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
      *     android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code
      *     AppFunctionException.ERROR_DENIED}.
-     *     <p>If the caller only has {@code android.permission.EXECUTE_APP_FUNCTIONS} but the
-     *     function requires {@code android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED}, the execution
+     *     <p>If the caller only has {@code android.permission.EXECUTE_APP_FUNCTIONS}, the execution
      *     result will contain {@code AppFunctionException.ERROR_DENIED}
      *     <p>If the function requested for execution is disabled, then the execution result will
      *     contain {@code AppFunctionException.ERROR_DISABLED}
      *     <p>If the cancellation signal is issued, the operation is cancelled and no response is
      *     returned to the caller.
      */
-    @RequiresPermission(
-            anyOf = {
-                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
-                Manifest.permission.EXECUTE_APP_FUNCTIONS
-            },
-            conditional = true)
+    @RequiresPermission(value = Manifest.permission.EXECUTE_APP_FUNCTIONS, conditional = true)
     @UserHandleAware
     public void executeAppFunction(
             @NonNull ExecuteAppFunctionRequest request,
@@ -222,9 +214,8 @@
      * Returns a boolean through a callback, indicating whether the app function is enabled.
      *
      * <p>This method can only check app functions owned by the caller, or those where the caller
-     * has visibility to the owner package and holds either the {@link
-     * Manifest.permission#EXECUTE_APP_FUNCTIONS} or {@link
-     * Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} permission.
+     * has visibility to the owner package and holds the
+     * {@link Manifest.permission#EXECUTE_APP_FUNCTIONS} permission.
      *
      * <p>If the operation fails, the callback's {@link OutcomeReceiver#onError} is called with
      * errors:
@@ -241,12 +232,7 @@
      * @param executor the executor to run the request
      * @param callback the callback to receive the function enabled check result
      */
-    @RequiresPermission(
-            anyOf = {
-                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
-                Manifest.permission.EXECUTE_APP_FUNCTIONS
-            },
-            conditional = true)
+    @RequiresPermission(value = Manifest.permission.EXECUTE_APP_FUNCTIONS, conditional = true)
     public void isAppFunctionEnabled(
             @NonNull String functionIdentifier,
             @NonNull String targetPackage,
diff --git a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
index 64dece9..cc3ca03 100644
--- a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
+++ b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
@@ -54,9 +54,8 @@
      * Returns (through a callback) a boolean indicating whether the app function is enabled.
      *
      * This method can only check app functions owned by the caller, or those where the caller
-     * has visibility to the owner package and holds either the {@link
-     * Manifest.permission#EXECUTE_APP_FUNCTIONS} or {@link
-     * Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} permission.
+     * has visibility to the owner package and holds the {@link
+     * Manifest.permission#EXECUTE_APP_FUNCTIONS} permission.
      *
      * <p>If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
      *
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
index 3ddda22..7743d48 100644
--- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -90,8 +90,7 @@
      * we need to have per-package app function schemas.
      *
      * <p>This schema should be set visible to callers from the package owner itself and for callers
-     * with {@link android.Manifest.permission#EXECUTE_APP_FUNCTIONS} or {@link
-     * android.Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} permissions.
+     * with the permission {@link android.Manifest.permission#EXECUTE_APP_FUNCTIONS}.
      *
      * @param packageName The package name to create a schema for.
      */
@@ -105,9 +104,8 @@
     /**
      * Creates a parent schema for all app function runtime schemas.
      *
-     * <p>This schema should be set visible to the owner itself and for callers with {@link
-     * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@link
-     * android.permission.EXECUTE_APP_FUNCTIONS} permissions.
+     * <p>This schema should be set visible to the owner itself and for callers with
+     * the permission {@link android.permission.EXECUTE_APP_FUNCTIONS}.
      */
     public static AppSearchSchema createParentAppFunctionRuntimeSchema() {
         return getAppFunctionRuntimeSchemaBuilder(RUNTIME_SCHEMA_TYPE).build();
diff --git a/core/java/android/app/appfunctions/IAppFunctionManager.aidl b/core/java/android/app/appfunctions/IAppFunctionManager.aidl
index 72335e4..098e1fe 100644
--- a/core/java/android/app/appfunctions/IAppFunctionManager.aidl
+++ b/core/java/android/app/appfunctions/IAppFunctionManager.aidl
@@ -34,7 +34,7 @@
     * @param request the request to execute an app function.
     * @param callback the callback to report the result.
     */
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional = true)")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = android.Manifest.permission.EXECUTE_APP_FUNCTIONS, conditional = true)")
     ICancellationSignal executeAppFunction(
         in ExecuteAppFunctionAidlRequest request,
         in IExecuteAppFunctionCallback callback
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 914ca73..733a348 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -62,13 +62,6 @@
 }
 
 flag {
-  name: "modes_ui_test"
-  namespace: "systemui"
-  description: "Guards new CTS tests for Modes; dependent on flags modes_api and modes_ui"
-  bug: "360862012"
-}
-
-flag {
   name: "modes_hsum"
   namespace: "systemui"
   description: "Fixes for modes (and DND/Zen in general) with HSUM or secondary users"
@@ -310,3 +303,17 @@
   description: "removes sbnholder from NLS"
   bug: "362981561"
 }
+
+flag {
+  name: "nm_summarization"
+  namespace: "systemui"
+  description: "Allows the NAS to summarize notifications"
+  bug: "390417189"
+}
+
+flag {
+  name: "nm_summarization_ui"
+  namespace: "systemui"
+  description: "Shows summarized notifications in the UI"
+  bug: "390217880"
+}
diff --git a/core/java/android/app/supervision/ISupervisionManager.aidl b/core/java/android/app/supervision/ISupervisionManager.aidl
index 4598421..e583302 100644
--- a/core/java/android/app/supervision/ISupervisionManager.aidl
+++ b/core/java/android/app/supervision/ISupervisionManager.aidl
@@ -22,4 +22,6 @@
  */
 interface ISupervisionManager {
     boolean isSupervisionEnabledForUser(int userId);
+    void setSupervisionEnabledForUser(int userId, boolean enabled);
+    String getActiveSupervisionAppPackage(int userId);
 }
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 92241f3..a4efd77 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -16,6 +16,7 @@
 
 package android.app.supervision;
 
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.annotation.UserHandleAware;
@@ -98,4 +99,49 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Sets whether the device is supervised for the current user.
+     *
+     * @hide
+     */
+    @UserHandleAware
+    public void setSupervisionEnabled(boolean enabled) {
+        setSupervisionEnabledForUser(mContext.getUserId(), enabled);
+    }
+
+    /**
+     * Sets whether the device is supervised for a given user.
+     *
+     * <p>The caller must be from the same user as the target or hold the {@link
+     * android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+     *
+     * @hide
+     */
+    @RequiresPermission(
+            value = android.Manifest.permission.INTERACT_ACROSS_USERS,
+            conditional = true)
+    public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
+        try {
+            mService.setSupervisionEnabledForUser(userId, enabled);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the package name of the app that is acting as the active supervision app or null if
+     * supervision is disabled.
+     *
+     * @hide
+     */
+    @UserHandleAware
+    @Nullable
+    public String getActiveSupervisionAppPackage() {
+        try {
+            return mService.getActiveSupervisionAppPackage(mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
index 18182b8..232883c 100644
--- a/core/java/android/app/supervision/flags.aconfig
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -48,3 +48,19 @@
   description: "Flag that enables the Supervision settings screen with top-level Android settings entry point"
   bug: "383404606"
 }
+
+flag {
+  name: "enable_app_approval"
+  is_exported: true
+  namespace: "supervision"
+  description: "Flag to enable the App Approval settings in Android settings UI"
+  bug: "390185393"
+}
+
+flag {
+  name: "enable_supervision_pin_recovery_screen"
+  is_exported: true
+  namespace: "supervision"
+  description: "Flag that enables the Supervision pin recovery screen with Supervision settings entry point"
+  bug: "390500290"
+}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index df1028e..b9b5c6a 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityOptions;
-import android.app.LoadedApk;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -753,9 +752,6 @@
      */
     protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
         try {
-            ApplicationInfo expectedAppInfo = mInfo.providerInfo.applicationInfo;
-            LoadedApk.checkAndUpdateApkPaths(expectedAppInfo);
-            // Return if cloned successfully, otherwise default
             Context newContext = mContext.createApplicationContext(
                     mInfo.providerInfo.applicationInfo,
                     Context.CONTEXT_RESTRICTED);
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index a098a60..67dea32 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -16,6 +16,7 @@
 
 package android.companion;
 
+import static android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES;
 import static android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED;
 
 import static com.android.internal.util.CollectionUtils.emptyIfNull;
@@ -28,7 +29,10 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.StringDef;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
 import android.annotation.UserIdInt;
+import android.app.KeyguardManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.drawable.Icon;
 import android.os.Build;
@@ -214,6 +218,11 @@
     private final boolean mForceConfirmation;
 
     /**
+     * Whether to skip the role grant, permission checks and consent dialog.
+     */
+    private final boolean mSkipRoleGrant;
+
+    /**
      * The app package name of the application the association will belong to.
      * Populated by the system.
      * @hide
@@ -283,6 +292,7 @@
             @Nullable CharSequence displayName,
             boolean selfManaged,
             boolean forceConfirmation,
+            boolean skipRoleGrant,
             @Nullable Icon deviceIcon) {
         mSingleDevice = singleDevice;
         mDeviceFilters = requireNonNull(deviceFilters);
@@ -290,6 +300,7 @@
         mDisplayName = displayName;
         mSelfManaged = selfManaged;
         mForceConfirmation = forceConfirmation;
+        mSkipRoleGrant = skipRoleGrant;
         mCreationTime = System.currentTimeMillis();
         mDeviceIcon = deviceIcon;
     }
@@ -333,6 +344,18 @@
     }
 
     /**
+     * Whether to skip the role grant, permission checks and consent dialog.
+     *
+     * @see Builder#setSkipRoleGrant(boolean)
+     * @hide
+     */
+    @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+    @TestApi
+    public boolean isSkipRoleGrant() {
+        return mSkipRoleGrant;
+    }
+
+    /**
      * Whether only a single device should match the provided filter.
      *
      * When scanning for a single device with a specific {@link BluetoothDeviceFilter} mac
@@ -407,6 +430,7 @@
         private CharSequence mDisplayName;
         private boolean mSelfManaged = false;
         private boolean mForceConfirmation = false;
+        private boolean mSkipRoleGrant = false;
         private Icon mDeviceIcon = null;
 
         public Builder() {}
@@ -494,6 +518,27 @@
         }
 
         /**
+         * Do not attempt to grant the role corresponding to the device profile.
+         *
+         * <p>This will skip the permission checks and consent dialog but will not fail if the
+         * role cannot be granted.</p>
+         *
+         * <p>Requires that the device not to have secure lock screen and that there no locked SIM
+         * card. See {@link KeyguardManager#isKeyguardSecure()}</p>
+         *
+         * @hide
+         */
+        @RequiresPermission(ASSOCIATE_COMPANION_DEVICES)
+        @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+        @TestApi
+        @NonNull
+        public Builder setSkipRoleGrant(boolean skipRoleGrant) {
+            checkNotUsed();
+            mSkipRoleGrant = skipRoleGrant;
+            return this;
+        }
+
+        /**
          * Set the device icon for the self-managed device and to display the icon in the
          * self-managed association dialog.
          * <p>The given device icon will be resized to 24dp x 24dp.
@@ -521,7 +566,8 @@
                         + "provide the display name of the device");
             }
             return new AssociationRequest(mSingleDevice, emptyIfNull(mDeviceFilters),
-                    mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation, mDeviceIcon);
+                    mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation, mSkipRoleGrant,
+                    mDeviceIcon);
         }
     }
 
@@ -597,6 +643,7 @@
                 + ", associatedDevice = " + mAssociatedDevice
                 + ", selfManaged = " + mSelfManaged
                 + ", forceConfirmation = " + mForceConfirmation
+                + ", skipRoleGrant = " + mSkipRoleGrant
                 + ", packageName = " + mPackageName
                 + ", userId = " + mUserId
                 + ", deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription
@@ -617,6 +664,7 @@
                 && Objects.equals(mAssociatedDevice, that.mAssociatedDevice)
                 && mSelfManaged == that.mSelfManaged
                 && mForceConfirmation == that.mForceConfirmation
+                && mSkipRoleGrant == that.mSkipRoleGrant
                 && Objects.equals(mPackageName, that.mPackageName)
                 && mUserId == that.mUserId
                 && Objects.equals(mDeviceProfilePrivilegesDescription,
@@ -637,6 +685,7 @@
         _hash = 31 * _hash + Objects.hashCode(mAssociatedDevice);
         _hash = 31 * _hash + Boolean.hashCode(mSelfManaged);
         _hash = 31 * _hash + Boolean.hashCode(mForceConfirmation);
+        _hash = 31 * _hash + Boolean.hashCode(mSkipRoleGrant);
         _hash = 31 * _hash + Objects.hashCode(mPackageName);
         _hash = 31 * _hash + mUserId;
         _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription);
@@ -659,6 +708,7 @@
         if (mAssociatedDevice != null) flg |= 0x40;
         if (mPackageName != null) flg |= 0x80;
         if (mDeviceProfilePrivilegesDescription != null) flg |= 0x100;
+        if (mSkipRoleGrant) flg |= 0x200;
 
         dest.writeInt(flg);
         dest.writeParcelableList(mDeviceFilters, flags);
@@ -692,6 +742,7 @@
         boolean selfManaged = (flg & 0x2) != 0;
         boolean forceConfirmation = (flg & 0x4) != 0;
         boolean skipPrompt = (flg & 0x8) != 0;
+        boolean skipRoleGrant = (flg & 0x200) != 0;
         List<DeviceFilter<?>> deviceFilters = new ArrayList<>();
         in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(),
                 (Class<android.companion.DeviceFilter<?>>) (Class<?>)
@@ -714,6 +765,7 @@
         this.mAssociatedDevice = associatedDevice;
         this.mSelfManaged = selfManaged;
         this.mForceConfirmation = forceConfirmation;
+        this.mSkipRoleGrant = skipRoleGrant;
         this.mPackageName = packageName;
         this.mUserId = userId;
         com.android.internal.util.AnnotationValidations.validate(
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 6da2a07..1cf4282 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -18,13 +18,6 @@
 }
 
 flag {
-     namespace: "virtual_devices"
-     name: "media_projection_keyguard_restrictions"
-     description: "Auto-stop MP when the device locks"
-     bug: "348335290"
-}
-
-flag {
     namespace: "virtual_devices"
     name: "virtual_display_insets"
     description: "APIs for specifying virtual display insets (via cutout)"
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index a126363..efcaa0e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2722,10 +2722,10 @@
 
     /** @hide - designated user version */
     @UnsupportedAppUsage
-    public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
+    public final void registerContentObserver(Uri uri, boolean notifyForDescendants,
             ContentObserver observer, @UserIdInt int userHandle) {
         try {
-            getContentService().registerContentObserver(uri, notifyForDescendents,
+            getContentService().registerContentObserver(uri, notifyForDescendants,
                     observer.getContentObserver(), userHandle, mTargetSdkVersion);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 8d54673..8378695 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4270,6 +4270,7 @@
             //@hide: STATUS_BAR_SERVICE,
             THREAD_NETWORK_SERVICE,
             CONNECTIVITY_SERVICE,
+            TETHERING_SERVICE,
             PAC_PROXY_SERVICE,
             VCN_MANAGEMENT_SERVICE,
             //@hide: IP_MEMORY_STORE_SERVICE,
@@ -4964,10 +4965,10 @@
     /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.TetheringManager}
      * for managing tethering functions.
-     * @hide
+     *
      * @see android.net.TetheringManager
      */
-    @SystemApi
+    @SuppressLint("UnflaggedApi")
     public static final String TETHERING_SERVICE = "tethering";
 
     /**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4696882..0312ad7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -12426,8 +12426,8 @@
     }
 
     private void collectNestedIntentKeysRecur(Set<Intent> visited, boolean forceUnparcel) {
-        addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
         if (mExtras != null && (forceUnparcel || !mExtras.isParcelled()) && !mExtras.isEmpty()) {
+            addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
             for (String key : mExtras.keySet()) {
                 Object value;
                 try {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 219b204..b1ea6e9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2628,6 +2628,15 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                targetCode)) {
+            Slog.w(TAG, "Package requires development platform " + targetCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return Build.VERSION.SDK_INT;
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + targetCode
@@ -2699,6 +2708,15 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                minCode)) {
+            Slog.w(TAG, "Package requires min development platform " + minCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return Build.VERSION.SDK_INT;
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 8a3a3ad..582a1a9 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -183,6 +183,12 @@
      *
      * <p>This is not necessarily the system user. For example, it will not be the system user on
      * devices for which {@link UserManager#isHeadlessSystemUserMode()} returns true.
+     *
+     * <p>NB: Features should ideally not limit functionality to the main user. Ideally, they
+     * should either work for all users or for all admin users. If a feature should only work for
+     * select users, its determination of which user should be done intelligently or be
+     * customizable. Not all devices support a main user, and the idea of singling out one user as
+     * special is contrary to overall multiuser goals.
      */
     public static final int FLAG_MAIN = 0x00004000;
 
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index e30f871..d2d3a68 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -316,6 +316,15 @@
             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                        minCode)) {
+            Slog.w(TAG, "Parsed package requires min development platform " + minCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return input.success(Build.VERSION.SDK_INT);
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -398,19 +407,27 @@
             return input.success(targetVers);
         }
 
+        // If it's a pre-release SDK and the codename matches this platform, it
+        // definitely targets this SDK.
+        if (matchTargetCode(platformSdkCodenames, targetCode)) {
+            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        }
+
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                        targetCode)) {
+            Slog.w(TAG, "Parsed package requires development platform " + targetCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return input.success(Build.VERSION.SDK_INT);
+        }
+
         try {
             if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
                 return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
             }
         } catch (IllegalArgumentException e) {
-            // isAtMost() throws it when encountering an older SDK codename
-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());
-        }
-
-        // If it's a pre-release SDK and the codename matches this platform, it
-        // definitely targets this SDK.
-        if (matchTargetCode(platformSdkCodenames, targetCode)) {
-            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
         }
 
         // Otherwise, we're looking at an incompatible pre-release SDK.
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index 430ed2b..449423f1 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -133,6 +133,7 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
+    is_exported: true
 }
 
 flag {
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index f649e47..7dc6afb 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -178,6 +178,15 @@
     @FlaggedApi(Flags.FLAG_IDENTITY_CHECK_API)
     int BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS = 21;
 
+
+    /**
+     * The user pressed the more options button on prompt content. This is a placeholder that is
+     * currently only used by the support library.
+     *
+     * @hide
+     */
+    int BIOMETRIC_ERROR_CONTENT_VIEW_MORE_OPTIONS_BUTTON = 22;
+
     /**
      * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
      * because the authentication attempt was unsuccessful.
@@ -209,6 +218,7 @@
             BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
             BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE,
             BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS,
+            BIOMETRIC_ERROR_CONTENT_VIEW_MORE_OPTIONS_BUTTON,
             BIOMETRIC_PAUSED_REJECTED})
     @Retention(RetentionPolicy.SOURCE)
     @interface Errors {}
@@ -324,6 +334,5 @@
      * Returned from {@link BiometricManager#getLastAuthenticationTime(int)} when there has
      * been no successful authentication for the given authenticator since boot.
      */
-    @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
     long BIOMETRIC_NO_AUTHENTICATION = -1;
 }
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index c690c67..cefe20c 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -116,7 +116,6 @@
      * Returned from {@link BiometricManager#getLastAuthenticationTime(int)} when no matching
      * successful authentication has been performed since boot.
      */
-    @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
     public static final long BIOMETRIC_NO_AUTHENTICATION =
             BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
 
@@ -777,7 +776,6 @@
      */
     @RequiresPermission(USE_BIOMETRIC)
     @ElapsedRealtimeLong
-    @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
     public long getLastAuthenticationTime(
             @BiometricManager.Authenticators.Types int authenticators) {
         if (authenticators == 0
diff --git a/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java b/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
index de93234..d3fb935 100644
--- a/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
+++ b/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.admin.DevicePolicyManager;
+import android.app.supervision.SupervisionManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Build;
@@ -55,27 +56,44 @@
         return null;
     }
 
-    public static boolean parentConsentRequired(@NonNull Context context,
-            @NonNull DevicePolicyManager dpm, @BiometricAuthenticator.Modality int modality,
+    /** @return true if parental consent is required in order for biometric sensors to be used. */
+    public static boolean parentConsentRequired(
+            @NonNull Context context,
+            @NonNull DevicePolicyManager dpm,
+            @Nullable SupervisionManager sm,
+            @BiometricAuthenticator.Modality int modality,
             @NonNull UserHandle userHandle) {
         if (getTestComponentName(context, userHandle.getIdentifier()) != null) {
             return true;
         }
 
-        return parentConsentRequired(dpm, modality, userHandle);
+        return parentConsentRequired(dpm, sm, modality, userHandle);
     }
 
     /**
      * @return true if parental consent is required in order for biometric sensors to be used.
      */
-    public static boolean parentConsentRequired(@NonNull DevicePolicyManager dpm,
-            @BiometricAuthenticator.Modality int modality, @NonNull UserHandle userHandle) {
-        final ComponentName cn = getSupervisionComponentName(dpm, userHandle);
-        if (cn == null) {
-            return false;
+    public static boolean parentConsentRequired(
+            @NonNull DevicePolicyManager dpm,
+            @Nullable SupervisionManager sm,
+            @BiometricAuthenticator.Modality int modality,
+            @NonNull UserHandle userHandle) {
+        final int keyguardDisabledFeatures;
+
+        if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) {
+            if (sm != null && !sm.isSupervisionEnabledForUser(userHandle.getIdentifier())) {
+                return false;
+            }
+            // Check for keyguard features disabled by any admin.
+            keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(/* admin= */ null);
+        } else {
+            final ComponentName cn = getSupervisionComponentName(dpm, userHandle);
+            if (cn == null) {
+                return false;
+            }
+            keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(cn);
         }
 
-        final int keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(cn);
         final boolean dpmFpDisabled = containsFlag(keyguardDisabledFeatures,
                 DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
         final boolean dpmFaceDisabled = containsFlag(keyguardDisabledFeatures,
@@ -97,7 +115,9 @@
         return consentRequired;
     }
 
+    /** @deprecated Use {@link SupervisionManager} to check for supervision. */
     @Nullable
+    @Deprecated
     public static ComponentName getSupervisionComponentName(@NonNull DevicePolicyManager dpm,
             @NonNull UserHandle userHandle) {
         return dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle);
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 73b6417..4815f3e 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -2,14 +2,6 @@
 container: "system"
 
 flag {
-    name: "last_authentication_time"
-    is_exported: true
-    namespace: "wallet_integration"
-    description: "Feature flag for adding getLastAuthenticationTime API to BiometricManager"
-    bug: "301979982"
-}
-
-flag {
   name: "add_key_agreement_crypto_object"
   is_exported: true
   namespace: "biometrics"
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 5533a64..210653b 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -5256,9 +5256,6 @@
      * <p>DYNAMIC_RANGE_PROFILE: {STANDARD, HLG10}</p>
      * </li>
      * </ul>
-     * <p>All of the above configurations can be set up with a SessionConfiguration. The list of
-     * OutputConfiguration contains the stream configurations and DYNAMIC_RANGE_PROFILE, and
-     * the AE_TARGET_FPS_RANGE and VIDEO_STABILIZATION_MODE are set as session parameters.</p>
      * <p>When set to BAKLAVA, the additional stream combinations below are verified
      * by the compliance tests:</p>
      * <table>
@@ -5268,6 +5265,8 @@
      * <th style="text-align: center;">Size</th>
      * <th style="text-align: center;">Target 2</th>
      * <th style="text-align: center;">Size</th>
+     * <th style="text-align: center;">Target 3</th>
+     * <th style="text-align: center;">Size</th>
      * </tr>
      * </thead>
      * <tbody>
@@ -5276,15 +5275,34 @@
      * <td style="text-align: center;">S1080P</td>
      * <td style="text-align: center;">PRIV</td>
      * <td style="text-align: center;">S1080P</td>
+     * <td style="text-align: center;"></td>
+     * <td style="text-align: center;"></td>
      * </tr>
      * <tr>
      * <td style="text-align: center;">PRIV</td>
      * <td style="text-align: center;">S1080P</td>
      * <td style="text-align: center;">PRIV</td>
      * <td style="text-align: center;">S1440P</td>
+     * <td style="text-align: center;"></td>
+     * <td style="text-align: center;"></td>
+     * </tr>
+     * <tr>
+     * <td style="text-align: center;">PRIV</td>
+     * <td style="text-align: center;">S1080P</td>
+     * <td style="text-align: center;">YUV</td>
+     * <td style="text-align: center;">S1080P</td>
+     * <td style="text-align: center;">S1080P</td>
+     * <td style="text-align: center;">PRIV</td>
      * </tr>
      * </tbody>
      * </table>
+     * <ul>
+     * <li>VIDEO_STABILIZATION_MODE: {OFF, ON} for the newly added stream combinations given the
+     * presence of dedicated video stream</li>
+     * </ul>
+     * <p>All of the above configurations can be set up with a SessionConfiguration. The list of
+     * OutputConfiguration contains the stream configurations and DYNAMIC_RANGE_PROFILE, and
+     * the AE_TARGET_FPS_RANGE and VIDEO_STABILIZATION_MODE are set as session parameters.</p>
      * <p>This key is available on all devices.</p>
      */
     @PublicKey
diff --git a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
index 365f870..b40c7d3 100644
--- a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.graphics.ColorSpace;
 import android.graphics.ImageFormat.Format;
 import android.hardware.DataSpace.NamedDataSpace;
@@ -228,6 +229,7 @@
      *
      * @hide
      */
+    @TestApi
     public SharedSessionConfiguration(int sharedColorSpace,
             @NonNull long[] sharedOutputConfigurations) {
         mColorSpace = sharedColorSpace;
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 14911de..b7edef6 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -122,16 +122,11 @@
                         HubEndpointInfo initiator,
                         @Nullable String serviceDescriptor)
                         throws RemoteException {
-                    boolean sessionExists;
-                    synchronized (mLock) {
-                        sessionExists = mActiveSessions.contains(sessionId);
-                        // TODO(b/378974199): Consider refactor these assertions
-                        if (sessionExists) {
-                            Log.w(
-                                    TAG,
-                                    "onSessionOpenComplete: session already exists, id="
-                                            + sessionId);
-                        }
+                    boolean sessionExists = getActiveSession(sessionId) != null;
+                    if (sessionExists) {
+                        Log.w(
+                                TAG,
+                                "onSessionOpenComplete: session already exists, id=" + sessionId);
                     }
 
                     if (!sessionExists && mLifecycleCallback != null) {
@@ -150,13 +145,7 @@
 
                 @Override
                 public void onSessionOpenComplete(int sessionId) throws RemoteException {
-                    final HubEndpointSession activeSession;
-
-                    // Retrieve the active session
-                    synchronized (mLock) {
-                        activeSession = mActiveSessions.get(sessionId);
-                    }
-                    // TODO(b/378974199): Consider refactor these assertions
+                    final HubEndpointSession activeSession = getActiveSession(sessionId);
                     if (activeSession == null) {
                         Log.w(
                                 TAG,
@@ -179,13 +168,7 @@
 
                 @Override
                 public void onSessionClosed(int sessionId, int reason) throws RemoteException {
-                    final HubEndpointSession activeSession;
-
-                    // Retrieve the active session
-                    synchronized (mLock) {
-                        activeSession = mActiveSessions.get(sessionId);
-                    }
-                    // TODO(b/378974199): Consider refactor these assertions
+                    final HubEndpointSession activeSession = getActiveSession(sessionId);
                     if (activeSession == null) {
                         Log.w(TAG, "onSessionClosed: session not active, id=" + sessionId);
                     }
@@ -211,12 +194,7 @@
                 @Override
                 public void onMessageReceived(int sessionId, HubMessage message)
                         throws RemoteException {
-                    final HubEndpointSession activeSession;
-
-                    // Retrieve the active session
-                    synchronized (mLock) {
-                        activeSession = mActiveSessions.get(sessionId);
-                    }
+                    final HubEndpointSession activeSession = getActiveSession(sessionId);
                     if (activeSession == null) {
                         Log.w(TAG, "onMessageReceived: session not active, id=" + sessionId);
                     }
@@ -233,6 +211,12 @@
                     }
                 }
 
+                private HubEndpointSession getActiveSession(int sessionId) {
+                    synchronized (mLock) {
+                        return mActiveSessions.get(sessionId);
+                    }
+                }
+
                 private void sendMessageDeliveryStatus(
                         int sessionId, HubMessage message, byte errorCode) {
                     if (message.isResponseRequired()) {
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 0590a06..a96de4b 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -146,6 +146,22 @@
             "android.hardware.display.category.PRESENTATION";
 
     /**
+     * Display category: Built in displays.
+     *
+     * <p>
+     *     This category can be used to identify displays that are built into the device. The
+     *     displays that are returned may be inactive or disabled at the current moment. The
+     *     returned displays are useful in identifying the various sizes of built-in displays. The
+     *     id from {@link Display#getDisplayId()} is not guaranteed to be stable and may change
+     *     when the display becomes active.
+     * </p>
+     * @see #getDisplays(String)
+     */
+    @FlaggedApi(com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_CATEGORY_BUILT_IN)
+    public static final String DISPLAY_CATEGORY_BUILT_IN_DISPLAYS =
+            "android.hardware.display.category.BUILT_IN_DISPLAYS";
+
+    /**
      * Display category: Rear displays.
      * <p>
      * This category can be used to identify complementary internal displays that are facing away
@@ -171,6 +187,8 @@
      * @see #getDisplays(String)
      * @hide
      */
+    @TestApi
+    @SuppressLint("UnflaggedApi")
     public static final String DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED =
             "android.hardware.display.category.ALL_INCLUDING_DISABLED";
 
@@ -729,10 +747,13 @@
      * @see #DISPLAY_CATEGORY_PRESENTATION
      */
     public Display[] getDisplays(String category) {
-        boolean includeDisabled = (category != null
-                && category.equals(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED));
+        boolean includeDisabled = shouldIncludeDisabledDisplays(category);
         final int[] displayIds = mGlobal.getDisplayIds(includeDisabled);
-        if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) {
+        if (Flags.displayCategoryBuiltIn()
+                && DISPLAY_CATEGORY_BUILT_IN_DISPLAYS.equals(category)) {
+            Display[] value = getDisplays(displayIds, DisplayManager::isBuiltInDisplay);
+            return value;
+        } else if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) {
             return getDisplays(displayIds, DisplayManager::isPresentationDisplay);
         } else if (DISPLAY_CATEGORY_REAR.equals(category)) {
             return getDisplays(displayIds, DisplayManager::isRearDisplay);
@@ -742,6 +763,16 @@
         return new Display[0];
     }
 
+    private boolean shouldIncludeDisabledDisplays(@Nullable String category) {
+        if (DISPLAY_CATEGORY_BUILT_IN_DISPLAYS.equals(category)) {
+            return true;
+        }
+        if (DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
+            return true;
+        }
+        return false;
+    }
+
     private Display[] getDisplays(int[] displayIds, Predicate<Display> predicate) {
         ArrayList<Display> tmpDisplays = new ArrayList<>();
         for (int displayId : displayIds) {
@@ -753,6 +784,13 @@
         return tmpDisplays.toArray(new Display[tmpDisplays.size()]);
     }
 
+    private static boolean isBuiltInDisplay(@Nullable Display display) {
+        if (display == null) {
+            return false;
+        }
+        return display.getType() == Display.TYPE_INTERNAL;
+    }
+
     private static boolean isPresentationDisplay(@Nullable Display display) {
         if (display == null || (display.getDisplayId() == DEFAULT_DISPLAY)
                 || (display.getFlags() & Display.FLAG_PRESENTATION) == 0) {
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index b39bd8c..555ff4b 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -33,6 +33,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.view.Display;
 
 import androidx.annotation.NonNull;
@@ -61,6 +62,7 @@
 public final class DisplayTopology implements Parcelable {
     private static final String TAG = "DisplayTopology";
     private static final float EPSILON = 0.0001f;
+    private static final float MAX_GAP = 5;
 
     @android.annotation.NonNull
     public static final Creator<DisplayTopology> CREATOR =
@@ -108,7 +110,7 @@
 
     public DisplayTopology() {}
 
-    public DisplayTopology(TreeNode root, int primaryDisplayId) {
+    public DisplayTopology(@Nullable TreeNode root, int primaryDisplayId) {
         mRoot = root;
         if (mRoot != null) {
             // Set mRoot's position and offset to predictable values, just so we don't leak state
@@ -181,6 +183,8 @@
         if (findDisplay(displayId, mRoot) == null) {
             return false;
         }
+
+        // Re-add the other displays to a new tree
         Queue<TreeNode> queue = new ArrayDeque<>();
         queue.add(mRoot);
         mRoot = null;
@@ -191,6 +195,7 @@
             }
             queue.addAll(node.mChildren);
         }
+
         if (mPrimaryDisplayId == displayId) {
             if (mRoot != null) {
                 mPrimaryDisplayId = mRoot.mDisplayId;
@@ -218,6 +223,9 @@
      *                                  IDs in this topology, no more, no less
      */
     public void rearrange(Map<Integer, PointF> newPos) {
+        if (mRoot == null) {
+            return;
+        }
         var availableParents = new ArrayList<TreeNode>();
 
         availableParents.addLast(mRoot);
@@ -447,11 +455,11 @@
             // Check that the offset is within bounds
             areTouching &= switch (targetDisplay.mPosition) {
                 case POSITION_LEFT, POSITION_RIGHT ->
-                        childBounds.bottom + EPSILON >= parentBounds.top
-                                && childBounds.top <= parentBounds.bottom + EPSILON;
+                        childBounds.bottom + EPSILON > parentBounds.top
+                                && childBounds.top < parentBounds.bottom + EPSILON;
                 case POSITION_TOP, POSITION_BOTTOM ->
-                        childBounds.right + EPSILON >= parentBounds.left
-                                && childBounds.left <= parentBounds.right + EPSILON;
+                        childBounds.right + EPSILON > parentBounds.left
+                                && childBounds.left < parentBounds.right + EPSILON;
                 default -> throw new IllegalStateException(
                         "Unexpected value: " + targetDisplay.mPosition);
             };
@@ -566,7 +574,7 @@
                     "DisplayTopology: attempting to add a display that already exists");
         }
         if (mRoot == null) {
-            mRoot = new TreeNode(displayId, width, height, /* position= */ 0, /* offset= */ 0);
+            mRoot = new TreeNode(displayId, width, height, POSITION_LEFT, /* offset= */ 0);
             mPrimaryDisplayId = displayId;
             if (shouldLog) {
                 Slog.i(TAG, "First display added: " + mRoot);
@@ -681,13 +689,112 @@
         }
     }
 
-    /** Returns the graph representation of the topology */
-    public DisplayTopologyGraph getGraph() {
-        // TODO(b/364907904): implement
-        return new DisplayTopologyGraph(mPrimaryDisplayId,
-                new DisplayTopologyGraph.DisplayNode[] { new DisplayTopologyGraph.DisplayNode(
-                        mRoot == null ? Display.DEFAULT_DISPLAY : mRoot.mDisplayId,
-                        new DisplayTopologyGraph.AdjacentDisplay[0])});
+    /**
+     * Check if two displays are touching.
+     * If the gap between two edges is <= {@link MAX_GAP}, they are still considered adjacent.
+     * The position indicates where the second display is touching the first one and the offset
+     * indicates where along the first display the second display is located.
+     * @param bounds1 The bounds of the first display
+     * @param bounds2 The bounds of the second display
+     * @return Empty list if the displays are not adjacent;
+     * List of one Pair(position, offset) if the displays are adjacent but not by a corner;
+     * List of two Pair(position, offset) if the displays are adjacent by a corner.
+     */
+    private List<Pair<Integer, Float>> findDisplayPlacements(RectF bounds1, RectF bounds2) {
+        List<Pair<Integer, Float>> placements = new ArrayList<>();
+        if (bounds1.top <= bounds2.bottom + MAX_GAP && bounds2.top <= bounds1.bottom + MAX_GAP) {
+            if (MathUtils.abs(bounds1.left - bounds2.right) <= MAX_GAP) {
+                placements.add(new Pair<>(POSITION_LEFT, bounds2.top - bounds1.top));
+            }
+            if (MathUtils.abs(bounds1.right - bounds2.left) <= MAX_GAP) {
+                placements.add(new Pair<>(POSITION_RIGHT, bounds2.top - bounds1.top));
+            }
+        }
+        if (bounds1.left <= bounds2.right + MAX_GAP && bounds2.left <= bounds1.right + MAX_GAP) {
+            if (MathUtils.abs(bounds1.top - bounds2.bottom) < MAX_GAP) {
+                placements.add(new Pair<>(POSITION_TOP, bounds2.left - bounds1.left));
+            }
+            if (MathUtils.abs(bounds1.bottom - bounds2.top) < MAX_GAP) {
+                placements.add(new Pair<>(POSITION_BOTTOM, bounds2.left - bounds1.left));
+            }
+        }
+        return placements;
+    }
+
+    /**
+     * @param densityPerDisplay The logical display densities, indexed by logical display ID
+     * @return The graph representation of the topology. If there is a corner adjacency, the same
+     * display will appear twice in the list of adjacent displays with both possible placements.
+     */
+    @Nullable
+    public DisplayTopologyGraph getGraph(SparseIntArray densityPerDisplay) {
+        // Sort the displays by position
+        SparseArray<RectF> bounds = getAbsoluteBounds();
+        Comparator<Integer> comparator = (displayId1, displayId2) -> {
+            RectF bounds1 = bounds.get(displayId1);
+            RectF bounds2 = bounds.get(displayId2);
+
+            int compareX = Float.compare(bounds1.left, bounds2.left);
+            if (compareX != 0) {
+                return compareX;
+            }
+            return Float.compare(bounds1.top, bounds2.top);
+        };
+        List<Integer> displayIds = new ArrayList<>(bounds.size());
+        for (int i = 0; i < bounds.size(); i++) {
+            displayIds.add(bounds.keyAt(i));
+        }
+        displayIds.sort(comparator);
+
+        SparseArray<List<DisplayTopologyGraph.AdjacentDisplay>> adjacentDisplaysPerId =
+                new SparseArray<>();
+        for (int id : displayIds) {
+            if (densityPerDisplay.get(id) == 0) {
+                Slog.w(TAG, "Cannot construct graph, no density for display " + id);
+                return null;
+            }
+            adjacentDisplaysPerId.append(id, new ArrayList<>(Math.min(10, displayIds.size())));
+        }
+
+        // Find touching displays
+        for (int i = 0; i < displayIds.size(); i++) {
+            int displayId1 = displayIds.get(i);
+            RectF bounds1 = bounds.get(displayId1);
+            List<DisplayTopologyGraph.AdjacentDisplay> adjacentDisplays1 =
+                    adjacentDisplaysPerId.get(displayId1);
+
+            for (int j = i + 1; j < displayIds.size(); j++) {
+                int displayId2 = displayIds.get(j);
+                RectF bounds2 = bounds.get(displayId2);
+                List<DisplayTopologyGraph.AdjacentDisplay> adjacentDisplays2 =
+                        adjacentDisplaysPerId.get(displayId2);
+
+                List<Pair<Integer, Float>> placements1 = findDisplayPlacements(bounds1, bounds2);
+                List<Pair<Integer, Float>> placements2 = findDisplayPlacements(bounds2, bounds1);
+                for (Pair<Integer, Float> placement : placements1) {
+                    adjacentDisplays1.add(new DisplayTopologyGraph.AdjacentDisplay(displayId2,
+                            /* position= */ placement.first, /* offsetDp= */ placement.second));
+                }
+                for (Pair<Integer, Float> placement : placements2) {
+                    adjacentDisplays2.add(new DisplayTopologyGraph.AdjacentDisplay(displayId1,
+                            /* position= */ placement.first, /* offsetDp= */ placement.second));
+                }
+                if (bounds2.left >= bounds1.right + EPSILON) {
+                    // This and the subsequent displays are already too far away
+                    break;
+                }
+            }
+        }
+
+        DisplayTopologyGraph.DisplayNode[] nodes =
+                new DisplayTopologyGraph.DisplayNode[adjacentDisplaysPerId.size()];
+        for (int i = 0; i < nodes.length; i++) {
+            int displayId = adjacentDisplaysPerId.keyAt(i);
+            nodes[i] = new DisplayTopologyGraph.DisplayNode(displayId,
+                    densityPerDisplay.get(displayId), adjacentDisplaysPerId.valueAt(i).toArray(
+                            new DisplayTopologyGraph.AdjacentDisplay[0]));
+        }
+        return new DisplayTopologyGraph(mPrimaryDisplayId, nodes);
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayTopologyGraph.java b/core/java/android/hardware/display/DisplayTopologyGraph.java
index 938e6d1..99528cc 100644
--- a/core/java/android/hardware/display/DisplayTopologyGraph.java
+++ b/core/java/android/hardware/display/DisplayTopologyGraph.java
@@ -16,6 +16,8 @@
 
 package android.hardware.display;
 
+import static android.hardware.display.DisplayTopology.TreeNode.positionToString;
+
 /**
  * Graph of the displays in {@link android.hardware.display.DisplayTopology} tree.
  *
@@ -27,6 +29,7 @@
      */
     public record DisplayNode(
             int displayId,
+            int density,
             AdjacentDisplay[] adjacentDisplays) {}
 
     /**
@@ -38,6 +41,18 @@
             // Side of the other display which touches this adjacent display.
             @DisplayTopology.TreeNode.Position
             int position,
-            // How many px this display is shifted along the touchingSide, can be negative.
-            float offsetPx) {}
+            // The distance from the top edge of the other display to the top edge of this display
+            // (in case of POSITION_LEFT or POSITION_RIGHT) or from the left edge of the parent
+            // display to the left edge of this display (in case of POSITION_TOP or
+            // POSITION_BOTTOM). The unit used is density-independent pixels (dp).
+            float offsetDp) {
+        @Override
+        public String toString() {
+            return "AdjacentDisplay{"
+                    + "displayId=" + displayId
+                    + ", position=" + positionToString(position)
+                    + ", offsetDp=" + offsetDp
+                    + '}';
+        }
+    }
 }
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 2bb28a1..7fc7e4d 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -106,6 +106,12 @@
     @EnforcePermission("SET_KEYBOARD_LAYOUT")
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
             + "android.Manifest.permission.SET_KEYBOARD_LAYOUT)")
+    void setKeyboardLayoutOverrideForInputDevice(in InputDeviceIdentifier identifier,
+            String keyboardLayoutDescriptor);
+
+    @EnforcePermission("SET_KEYBOARD_LAYOUT")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.SET_KEYBOARD_LAYOUT)")
     void setKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier, int userId,
             in InputMethodInfo imeInfo, in InputMethodSubtype imeSubtype,
             String keyboardLayoutDescriptor);
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index 3ef90e4..e794161 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -57,6 +57,8 @@
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.PointerIcon;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -1256,6 +1258,43 @@
     }
 
     /**
+     * Sets the keyboard layout override for the specified input device. This will set the
+     * keyboard layout as the default for the input device irrespective of the underlying IME
+     * configuration.
+     *
+     * <p>
+     * Prefer using {@link InputManager#setKeyboardLayoutForInputDevice(InputDeviceIdentifier, int,
+     * InputMethodInfo, InputMethodSubtype, String)} for normal use cases.
+     * </p><p>
+     * This method is to be used only for special cases where we knowingly want to set a
+     * particular keyboard layout for a keyboard, ignoring the IME configuration. e.g. Setting a
+     * default layout for an Android Emulator where we know the preferred H/W keyboard layout.
+     * </p><p>
+     * NOTE: This may affect the typing experience if the layout isn't compatible with the IME
+     * configuration.
+     * </p><p>
+     * NOTE: User can still change the keyboard layout configuration from the settings page.
+     * </p>
+     *
+     * @param identifier The identifier for the input device.
+     * @param keyboardLayoutDescriptor The keyboard layout descriptor to use.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
+    public void setKeyboardLayoutOverrideForInputDevice(@NonNull InputDeviceIdentifier identifier,
+            @NonNull String keyboardLayoutDescriptor) {
+        Objects.requireNonNull(identifier, "identifier should not be null");
+        Objects.requireNonNull(keyboardLayoutDescriptor,
+                "keyboardLayoutDescriptor should not be null");
+        try {
+            mIm.setKeyboardLayoutOverrideForInputDevice(identifier, keyboardLayoutDescriptor);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * TODO(b/330517633): Cleanup the unsupported API
      */
     @NonNull
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index cd48047..af40188 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -35,7 +35,6 @@
 import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
 import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiKeyGestures;
 import static com.android.input.flags.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
-import static com.android.input.flags.Flags.enableInputFilterRustImpl;
 import static com.android.input.flags.Flags.keyboardRepeatKeys;
 
 import android.Manifest;
@@ -883,7 +882,7 @@
      * @hide
      */
     public static boolean isAccessibilityBounceKeysFeatureEnabled() {
-        return keyboardA11yBounceKeysFlag() && enableInputFilterRustImpl();
+        return keyboardA11yBounceKeysFlag();
     }
 
     /**
@@ -967,7 +966,7 @@
      * @hide
      */
     public static boolean isAccessibilitySlowKeysFeatureFlagEnabled() {
-        return keyboardA11ySlowKeysFlag() && enableInputFilterRustImpl();
+        return keyboardA11ySlowKeysFlag();
     }
 
     /**
@@ -1053,7 +1052,7 @@
      * @hide
      */
     public static boolean isAccessibilityStickyKeysFeatureEnabled() {
-        return keyboardA11yStickyKeysFlag() && enableInputFilterRustImpl();
+        return keyboardA11yStickyKeysFlag();
     }
 
     /**
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 79323bf..ae017e8 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -57,16 +57,6 @@
 }
 
 flag {
-    namespace: "input_native"
-    name: "keyboard_layout_manager_multi_user_ime_setup"
-    description: "Update KeyboardLayoutManager to work correctly with multi-user IME setup"
-    bug: "354333072"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "modifier_shortcut_dump"
     namespace: "input"
     description: "Dump keyboard shortcuts in dumpsys window"
diff --git a/core/java/android/hardware/usb/OWNERS b/core/java/android/hardware/usb/OWNERS
index 37604bc..1de8a24 100644
--- a/core/java/android/hardware/usb/OWNERS
+++ b/core/java/android/hardware/usb/OWNERS
@@ -1,7 +1,7 @@
 # Bug component: 175220
 
-anothermark@google.com
+vmartensson@google.com
+nkapron@google.com
 febinthattil@google.com
-aprasath@google.com
+shubhankarm@google.com
 badhri@google.com
-kumarashishg@google.com
\ No newline at end of file
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 019ba00..f420b5d 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -16,9 +16,9 @@
 
 package android.inputmethodservice;
 
-import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.app.StatusBarManager.NAVBAR_BACK_DISMISS_IME;
+import static android.app.StatusBarManager.NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
+import static android.app.StatusBarManager.NAVBAR_IME_VISIBLE;
 import static android.view.WindowInsets.Type.captionBar;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 
@@ -242,11 +242,11 @@
                         NavigationBarView.class::isInstance);
                 if (navigationBarView != null) {
                     // TODO(b/213337792): Support InputMethodService#setBackDisposition().
-                    // TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
-                    final int hints = NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
+                    // TODO(b/213337792): Set NAVBAR_IME_VISIBLE only when necessary.
+                    final int flags = NAVBAR_BACK_DISMISS_IME | NAVBAR_IME_VISIBLE
                             | (mShouldShowImeSwitcherWhenImeIsShown
-                                    ? NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
-                    navigationBarView.setNavigationIconHints(hints);
+                                    ? NAVBAR_IME_SWITCHER_BUTTON_VISIBLE : 0);
+                    navigationBarView.setNavbarFlags(flags);
                     navigationBarView.prepareNavButtons(this);
                 }
             } else {
@@ -515,11 +515,11 @@
                         NavigationBarView.class::isInstance);
                 if (navigationBarView != null) {
                     // TODO(b/213337792): Support InputMethodService#setBackDisposition().
-                    // TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
-                    final int hints = NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
+                    // TODO(b/213337792): Set NAVBAR_IME_VISIBLE only when necessary.
+                    final int flags = NAVBAR_BACK_DISMISS_IME | NAVBAR_IME_VISIBLE
                             | (mShouldShowImeSwitcherWhenImeIsShown
-                                    ? NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
-                    navigationBarView.setNavigationIconHints(hints);
+                                    ? NAVBAR_IME_SWITCHER_BUTTON_VISIBLE : 0);
+                    navigationBarView.setNavbarFlags(flags);
                 }
             } else {
                 uninstallNavigationBarFrameIfNecessary();
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
index e7e46a9..960a5b3 100644
--- a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
@@ -16,6 +16,8 @@
 
 package android.inputmethodservice.navigationbar;
 
+import static android.app.StatusBarManager.NAVBAR_BACK_DISMISS_IME;
+import static android.app.StatusBarManager.NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
 import static android.inputmethodservice.navigationbar.NavigationBarConstants.DARK_MODE_ICON_COLOR_SINGLE_TONE;
 import static android.inputmethodservice.navigationbar.NavigationBarConstants.LIGHT_MODE_ICON_COLOR_SINGLE_TONE;
 import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVBAR_BACK_BUTTON_IME_OFFSET;
@@ -28,6 +30,7 @@
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.app.StatusBarManager;
+import android.app.StatusBarManager.NavbarFlags;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
@@ -63,7 +66,8 @@
     private int mCurrentRotation = -1;
 
     int mDisabledFlags = 0;
-    int mNavigationIconHints = StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+    @NavbarFlags
+    private int mNavbarFlags;
     private final int mNavBarMode = NAV_BAR_MODE_GESTURAL;
 
     private KeyButtonDrawable mBackIcon;
@@ -241,10 +245,9 @@
     }
 
     private void orientBackButton(KeyButtonDrawable drawable) {
-        final boolean useAltBack =
-                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+        final boolean isBackDismissIme = (mNavbarFlags & NAVBAR_BACK_DISMISS_IME) != 0;
         final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        float degrees = useAltBack ? (isRtl ? 90 : -90) : 0;
+        float degrees = isBackDismissIme ? (isRtl ? 90 : -90) : 0;
         if (drawable.getRotation() == degrees) {
             return;
         }
@@ -256,7 +259,7 @@
 
         // Animate the back button's rotation to the new degrees and only in portrait move up the
         // back button to line up with the other buttons
-        float targetY = useAltBack
+        float targetY = isBackDismissIme
                 ? -dpToPx(NAVBAR_BACK_BUTTON_IME_OFFSET, getResources())
                 : 0;
         ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable,
@@ -280,24 +283,26 @@
     }
 
     /**
-     * Updates the navigation icons based on {@code hints}.
+     * Sets the navigation bar state flags.
      *
-     * @param hints bit flags defined in {@link StatusBarManager}.
+     * @param flags the navigation bar state flags.
      */
-    public void setNavigationIconHints(int hints) {
-        if (hints == mNavigationIconHints) return;
-        final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
-        final boolean oldBackAlt =
-                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
-        if (newBackAlt != oldBackAlt) {
-            //onBackAltChanged(newBackAlt);
+    public void setNavbarFlags(@NavbarFlags int flags) {
+        if (flags == mNavbarFlags) {
+            return;
+        }
+        final boolean backDismissIme = (flags & StatusBarManager.NAVBAR_BACK_DISMISS_IME) != 0;
+        final boolean oldBackDismissIme =
+                (mNavbarFlags & StatusBarManager.NAVBAR_BACK_DISMISS_IME) != 0;
+        if (backDismissIme != oldBackDismissIme) {
+            //onBackDismissImeChanged(backDismissIme);
         }
 
         if (DEBUG) {
-            android.widget.Toast.makeText(getContext(), "Navigation icon hints = " + hints, 500)
+            android.widget.Toast.makeText(getContext(), "Navbar flags = " + flags, 500)
                     .show();
         }
-        mNavigationIconHints = hints;
+        mNavbarFlags = flags;
         updateNavButtonIcons();
     }
 
@@ -311,10 +316,12 @@
 
         getImeSwitchButton().setImageDrawable(mImeSwitcherIcon);
 
-        // Update IME button visibility, a11y and rotate button always overrides the appearance
-        final boolean imeSwitcherVisible =
-                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0;
-        getImeSwitchButton().setVisibility(imeSwitcherVisible ? View.VISIBLE : View.INVISIBLE);
+        // Update IME switcher button visibility, a11y and rotate button always overrides
+        // the appearance.
+        final boolean isImeSwitcherButtonVisible =
+                (mNavbarFlags & NAVBAR_IME_SWITCHER_BUTTON_VISIBLE) != 0;
+        getImeSwitchButton()
+                .setVisibility(isImeSwitcherButtonVisible ? View.VISIBLE : View.INVISIBLE);
 
         getBackButton().setVisibility(View.VISIBLE);
         getHomeHandle().setVisibility(View.INVISIBLE);
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 1cf293d..e79b2e7 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -142,6 +142,7 @@
     /** {@hide} */
     @VisibleForTesting
     public int mFlags;
+    private boolean mHasIntent = false;
 
     /**
      * Constructs a new, empty Bundle that uses a specific ClassLoader for
@@ -258,9 +259,20 @@
 
             // Keep as last statement to ensure visibility of other fields
             mParcelledData = parcelledData;
+            mHasIntent = from.mHasIntent;
         }
     }
 
+    /** @hide */
+    public boolean hasIntent() {
+        return mHasIntent;
+    }
+
+    /** @hide */
+    public void setHasIntent(boolean hasIntent) {
+        mHasIntent = hasIntent;
+    }
+
     /**
      * TODO: optimize this later (getting just the value part of a Bundle
      * with a single pair) once Bundle.forPair() above is implemented
@@ -1837,6 +1849,7 @@
                     parcel.writeInt(length);
                     parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
                     parcel.appendFrom(mParcelledData, 0, length);
+                    parcel.writeBoolean(mHasIntent);
                 }
                 return;
             }
@@ -1851,7 +1864,6 @@
         int lengthPos = parcel.dataPosition();
         parcel.writeInt(-1); // placeholder, will hold length
         parcel.writeInt(BUNDLE_MAGIC);
-
         int startPos = parcel.dataPosition();
         parcel.writeArrayMapInternal(map);
         int endPos = parcel.dataPosition();
@@ -1861,6 +1873,7 @@
         int length = endPos - startPos;
         parcel.writeInt(length);
         parcel.setDataPosition(endPos);
+        parcel.writeBoolean(mHasIntent);
     }
 
     /**
@@ -1904,6 +1917,7 @@
                 mOwnsLazyValues = false;
                 initializeFromParcelLocked(parcel, /*ownsParcel*/ false, isNativeBundle);
             }
+            mHasIntent = parcel.readBoolean();
             return;
         }
 
@@ -1922,6 +1936,7 @@
         mOwnsLazyValues = true;
         mParcelledByNative = isNativeBundle;
         mParcelledData = p;
+        mHasIntent = parcel.readBoolean();
     }
 
     /** {@hide} */
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index f913fcf..86b8fad 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -161,6 +161,7 @@
     private final List<UserBatteryConsumer> mUserBatteryConsumers;
     private final AggregateBatteryConsumer[] mAggregateBatteryConsumers;
     private final BatteryStatsHistory mBatteryStatsHistory;
+    private final long mPreferredHistoryDurationMs;
     private final BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout;
     private CursorWindow mBatteryConsumersCursorWindow;
 
@@ -174,6 +175,7 @@
         mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah;
         mDischargeDurationMs = builder.mDischargeDurationMs;
         mBatteryStatsHistory = builder.mBatteryStatsHistory;
+        mPreferredHistoryDurationMs = builder.mPreferredHistoryDurationMs;
         mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs;
         mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs;
         mCustomPowerComponentNames = builder.mCustomPowerComponentNames;
@@ -402,8 +404,10 @@
 
         if (source.readBoolean()) {
             mBatteryStatsHistory = BatteryStatsHistory.createFromBatteryUsageStatsParcel(source);
+            mPreferredHistoryDurationMs = source.readLong();
         } else {
             mBatteryStatsHistory = null;
+            mPreferredHistoryDurationMs = 0;
         }
     }
 
@@ -428,7 +432,7 @@
 
         if (mBatteryStatsHistory != null) {
             dest.writeBoolean(true);
-            mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest);
+            mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest, mPreferredHistoryDurationMs);
         } else {
             dest.writeBoolean(false);
         }
@@ -919,6 +923,7 @@
         private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
                 new SparseArray<>();
         private BatteryStatsHistory mBatteryStatsHistory;
+        private long mPreferredHistoryDurationMs;
 
         public Builder(@NonNull String[] customPowerComponentNames) {
             this(customPowerComponentNames, false, false, false, 0);
@@ -1092,8 +1097,10 @@
          * Sets the parceled recent history.
          */
         @NonNull
-        public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory) {
+        public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory,
+                long preferredHistoryDurationMs) {
             mBatteryStatsHistory = batteryStatsHistory;
+            mPreferredHistoryDurationMs = preferredHistoryDurationMs;
             return this;
         }
 
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 6e67578..5aed39b 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -25,6 +25,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Query parameters for the {@link BatteryStatsManager#getBatteryUsageStats()} call.
@@ -77,6 +78,7 @@
     public static final int FLAG_BATTERY_USAGE_STATS_ACCUMULATED = 0x0080;
 
     private static final long DEFAULT_MAX_STATS_AGE_MS = 5 * 60 * 1000;
+    private static final long DEFAULT_PREFERRED_HISTORY_DURATION_MS = TimeUnit.HOURS.toMillis(2);
 
     private final int mFlags;
     @NonNull
@@ -89,6 +91,7 @@
     private long mMonotonicEndTime;
     private final double mMinConsumedPowerThreshold;
     private final @BatteryConsumer.PowerComponentId int[] mPowerComponents;
+    private final long mPreferredHistoryDurationMs;
 
     private BatteryUsageStatsQuery(@NonNull Builder builder) {
         mFlags = builder.mFlags;
@@ -101,6 +104,7 @@
         mMonotonicStartTime = builder.mMonotonicStartTime;
         mMonotonicEndTime = builder.mMonotonicEndTime;
         mPowerComponents = builder.mPowerComponents;
+        mPreferredHistoryDurationMs = builder.mPreferredHistoryDurationMs;
     }
 
     @BatteryUsageStatsFlags
@@ -197,6 +201,13 @@
         return mAggregatedToTimestamp;
     }
 
+    /**
+     * Returns the preferred duration of battery history (tail) to be included in the query result.
+     */
+    public long getPreferredHistoryDurationMs() {
+        return mPreferredHistoryDurationMs;
+    }
+
     @Override
     public String toString() {
         return "BatteryUsageStatsQuery{"
@@ -209,6 +220,7 @@
                 + ", mMonotonicEndTime=" + mMonotonicEndTime
                 + ", mMinConsumedPowerThreshold=" + mMinConsumedPowerThreshold
                 + ", mPowerComponents=" + Arrays.toString(mPowerComponents)
+                + ", mMaxHistoryDurationMs=" + mPreferredHistoryDurationMs
                 + '}';
     }
 
@@ -223,6 +235,7 @@
         mAggregatedFromTimestamp = in.readLong();
         mAggregatedToTimestamp = in.readLong();
         mPowerComponents = in.createIntArray();
+        mPreferredHistoryDurationMs = in.readLong();
     }
 
     @Override
@@ -237,6 +250,7 @@
         dest.writeLong(mAggregatedFromTimestamp);
         dest.writeLong(mAggregatedToTimestamp);
         dest.writeIntArray(mPowerComponents);
+        dest.writeLong(mPreferredHistoryDurationMs);
     }
 
     @Override
@@ -271,6 +285,7 @@
         private long mAggregateToTimestamp;
         private double mMinConsumedPowerThreshold = 0;
         private @BatteryConsumer.PowerComponentId int[] mPowerComponents;
+        private long mPreferredHistoryDurationMs = DEFAULT_PREFERRED_HISTORY_DURATION_MS;
 
         /**
          * Builds a read-only BatteryUsageStatsQuery object.
@@ -311,6 +326,16 @@
         }
 
         /**
+         * Set the preferred amount of battery history to be included in the result, provided
+         * that `includeBatteryHistory` is also called. The actual amount of history included in
+         * the result may vary for performance reasons and may exceed the specified preference.
+         */
+        public Builder setPreferredHistoryDurationMs(long preferredHistoryDurationMs) {
+            mPreferredHistoryDurationMs = preferredHistoryDurationMs;
+            return this;
+        }
+
+        /**
          * Requests that per-process state data be included in the BatteryUsageStats, if
          * available. Check {@link BatteryUsageStats#isProcessStateDataIncluded()} on the result
          * to see if the data is available.
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 819d58d..a24dc57 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -398,7 +398,7 @@
         if ((bundle.mFlags & FLAG_HAS_BINDERS_KNOWN) == 0) {
             mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
         }
-        mFlags |= bundle.mFlags & FLAG_HAS_INTENT;
+        setHasIntent(hasIntent() || bundle.hasIntent());
     }
 
     /**
@@ -465,7 +465,7 @@
      * @hide
      */
     public boolean hasIntent() {
-        return (mFlags & FLAG_HAS_INTENT) != 0;
+        return super.hasIntent();
     }
 
     /** {@hide} */
@@ -591,7 +591,7 @@
         mFlags &= ~FLAG_HAS_FDS_KNOWN;
         mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
         if (intentClass != null && intentClass.isInstance(value)) {
-            mFlags |= FLAG_HAS_INTENT;
+            setHasIntent(true);
         }
     }
 
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 50b621c..877f130 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -39,7 +39,6 @@
 import com.android.internal.ravenwood.RavenwoodEnvironment;
 
 import dalvik.annotation.optimization.NeverCompile;
-import dalvik.annotation.optimization.NeverInline;
 
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
@@ -238,7 +237,6 @@
     private final MatchDeliverableMessages mMatchDeliverableMessages =
             new MatchDeliverableMessages();
 
-    @NeverInline
     private boolean isIdleConcurrent() {
         final long now = SystemClock.uptimeMillis();
 
@@ -269,7 +267,6 @@
         return true;
     }
 
-    @NeverInline
     private boolean isIdleLegacy() {
         synchronized (this) {
             final long now = SystemClock.uptimeMillis();
@@ -292,14 +289,12 @@
         }
     }
 
-    @NeverInline
     private void addIdleHandlerConcurrent(@NonNull IdleHandler handler) {
         synchronized (mIdleHandlersLock) {
             mIdleHandlers.add(handler);
         }
     }
 
-    @NeverInline
     private void addIdleHandlerLegacy(@NonNull IdleHandler handler) {
         synchronized (this) {
             mIdleHandlers.add(handler);
@@ -326,15 +321,11 @@
             addIdleHandlerLegacy(handler);
         }
     }
-
-    @NeverInline
     private void removeIdleHandlerConcurrent(@NonNull IdleHandler handler) {
         synchronized (mIdleHandlersLock) {
             mIdleHandlers.remove(handler);
         }
     }
-
-    @NeverInline
     private void removeIdleHandlerLegacy(@NonNull IdleHandler handler) {
         synchronized (this) {
             mIdleHandlers.remove(handler);
@@ -358,14 +349,12 @@
         }
     }
 
-    @NeverInline
     private boolean isPollingConcurrent() {
         // If the loop is quitting then it must not be idling.
         // We can assume mPtr != 0 when sQuitting is false.
         return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr);
     }
 
-    @NeverInline
     private boolean isPollingLegacy() {
         synchronized (this) {
             return isPollingLocked();
@@ -396,7 +385,6 @@
         // We can assume mPtr != 0 when mQuitting is false.
         return !mQuitting && nativeIsPolling(mPtr);
     }
-    @NeverInline
     private void addOnFileDescriptorEventListenerConcurrent(@NonNull FileDescriptor fd,
             @OnFileDescriptorEventListener.Events int events,
             @NonNull OnFileDescriptorEventListener listener) {
@@ -405,7 +393,6 @@
         }
     }
 
-    @NeverInline
     private void addOnFileDescriptorEventListenerLegacy(@NonNull FileDescriptor fd,
             @OnFileDescriptorEventListener.Events int events,
             @NonNull OnFileDescriptorEventListener listener) {
@@ -455,14 +442,12 @@
         }
     }
 
-    @NeverInline
     private void removeOnFileDescriptorEventListenerConcurrent(@NonNull FileDescriptor fd) {
         synchronized (mFileDescriptorRecordsLock) {
             updateOnFileDescriptorEventListenerLocked(fd, 0, null);
         }
     }
 
-    @NeverInline
     private void removeOnFileDescriptorEventListenerLegacy(@NonNull FileDescriptor fd) {
         synchronized (this) {
             updateOnFileDescriptorEventListenerLocked(fd, 0, null);
@@ -616,7 +601,7 @@
     /* This is only read/written from the Looper thread. For use with Concurrent MQ */
     private int mNextPollTimeoutMillis;
     private boolean mMessageDirectlyQueued;
-    private Message nextMessage(boolean peek) {
+    private Message nextMessage(boolean peek, boolean returnEarliest) {
         int i = 0;
 
         while (true) {
@@ -693,7 +678,7 @@
              * If we have a barrier we should return the async node (if it exists and is ready)
              */
             if (msgNode != null && msgNode.isBarrier()) {
-                if (asyncMsgNode != null && now >= asyncMsgNode.getWhen()) {
+                if (asyncMsgNode != null && (returnEarliest || now >= asyncMsgNode.getWhen())) {
                     found = asyncMsgNode;
                 } else {
                     next = asyncMsgNode;
@@ -707,7 +692,7 @@
                 earliest = pickEarliestNode(msgNode, asyncMsgNode);
 
                 if (earliest != null) {
-                    if (now >= earliest.getWhen()) {
+                    if (returnEarliest || now >= earliest.getWhen()) {
                         found = earliest;
                     } else {
                         next = earliest;
@@ -796,7 +781,6 @@
         }
     }
 
-    @NeverInline
     private Message nextConcurrent() {
         final long ptr = mPtr;
         if (ptr == 0) {
@@ -813,7 +797,7 @@
             mMessageDirectlyQueued = false;
             nativePollOnce(ptr, mNextPollTimeoutMillis);
 
-            Message msg = nextMessage(false);
+            Message msg = nextMessage(false, false);
             if (msg != null) {
                 msg.markInUse();
                 return msg;
@@ -871,7 +855,6 @@
         }
     }
 
-    @NeverInline
     private Message nextLegacy() {
         // Return here if the message loop has already quit and been disposed.
         // This can happen if the application tries to restart a looper after quit
@@ -1036,13 +1019,11 @@
         }
     }
 
-    @NeverInline
     private int postSyncBarrierConcurrent() {
         return postSyncBarrier(SystemClock.uptimeMillis());
 
     }
 
-    @NeverInline
     private int postSyncBarrierLegacy() {
         return postSyncBarrier(SystemClock.uptimeMillis());
     }
@@ -1162,7 +1143,6 @@
         }
     }
 
-    @NeverInline
     private void removeSyncBarrierConcurrent(int token) {
         boolean removed;
         MessageNode first;
@@ -1189,7 +1169,6 @@
         }
     }
 
-    @NeverInline
     private void removeSyncBarrierLegacy(int token) {
         synchronized (this) {
             Message prev = null;
@@ -1249,7 +1228,6 @@
 
     }
 
-    @NeverInline
     private boolean enqueueMessageConcurrent(Message msg, long when) {
         if (msg.isInUse()) {
             throw new IllegalStateException(msg + " This message is already in use.");
@@ -1258,7 +1236,6 @@
         return enqueueMessageUnchecked(msg, when);
     }
 
-    @NeverInline
     private boolean enqueueMessageLegacy(Message msg, long when) {
         synchronized (this) {
             if (msg.isInUse()) {
@@ -1397,27 +1374,27 @@
                 if (now >= msg.when) {
                     // Got a message.
                     mBlocked = false;
-                    if (prevMsg != null) {
-                        prevMsg.next = msg.next;
-                        if (prevMsg.next == null) {
-                            mLast = prevMsg;
-                        }
-                    } else {
-                        mMessages = msg.next;
-                        if (msg.next == null) {
-                            mLast = null;
-                        }
-                    }
-                    msg.next = null;
-                    msg.markInUse();
-                    if (msg.isAsynchronous()) {
-                        mAsyncMessageCount--;
-                    }
-                    if (TRACE) {
-                        Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
-                    }
-                    return msg;
                 }
+                if (prevMsg != null) {
+                    prevMsg.next = msg.next;
+                    if (prevMsg.next == null) {
+                        mLast = prevMsg;
+                    }
+                } else {
+                    mMessages = msg.next;
+                    if (msg.next == null) {
+                        mLast = null;
+                    }
+                }
+                msg.next = null;
+                msg.markInUse();
+                if (msg.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
+                if (TRACE) {
+                    Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+                }
+                return msg;
             }
         }
         return null;
@@ -1434,7 +1411,7 @@
         throwIfNotTest();
         Message ret;
         if (mUseConcurrent) {
-            ret = nextMessage(true);
+            ret = nextMessage(true, true);
         } else {
             ret = legacyPeekOrPoll(true);
         }
@@ -1452,7 +1429,7 @@
     Message pollForTest() {
         throwIfNotTest();
         if (mUseConcurrent) {
-            return nextMessage(false);
+            return nextMessage(false, true);
         } else {
             return legacyPeekOrPoll(false);
         }
@@ -1469,7 +1446,7 @@
         throwIfNotTest();
         if (mUseConcurrent) {
             // Call nextMessage to get the stack drained into our priority queues
-            nextMessage(true);
+            nextMessage(true, false);
 
             Iterator<MessageNode> queueIter = mPriorityQueue.iterator();
             MessageNode queueNode = iterateNext(queueIter);
@@ -1495,13 +1472,11 @@
     private final MatchHandlerWhatAndObject mMatchHandlerWhatAndObject =
             new MatchHandlerWhatAndObject();
 
-    @NeverInline
     private boolean hasMessagesConcurrent(Handler h, int what, Object object) {
         return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject,
                 false);
     }
 
-    @NeverInline
     private boolean hasMessagesLegacy(Handler h, int what, Object object) {
         synchronized (this) {
             Message p = mMessages;
@@ -1540,13 +1515,11 @@
     private final MatchHandlerWhatAndObjectEquals mMatchHandlerWhatAndObjectEquals =
             new MatchHandlerWhatAndObjectEquals();
 
-    @NeverInline
     private boolean hasEqualMessagesConcurrent(Handler h, int what, Object object) {
         return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals,
                 false);
     }
 
-    @NeverInline
     private boolean hasEqualMessagesLegacy(Handler h, int what, Object object) {
         synchronized (this) {
             Message p = mMessages;
@@ -1585,13 +1558,11 @@
     private final MatchHandlerRunnableAndObject mMatchHandlerRunnableAndObject =
             new MatchHandlerRunnableAndObject();
 
-    @NeverInline
     private boolean hasMessagesConcurrent(Handler h, Runnable r, Object object) {
         return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject,
                 false);
     }
 
-    @NeverInline
     private boolean hasMessagesLegacy(Handler h, Runnable r, Object object) {
         synchronized (this) {
             Message p = mMessages;
@@ -1626,12 +1597,10 @@
     }
     private final MatchHandler mMatchHandler = new MatchHandler();
 
-    @NeverInline
     private boolean hasMessagesConcurrent(Handler h) {
         return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false);
     }
 
-    @NeverInline
     private boolean hasMessagesLegacy(Handler h) {
         synchronized (this) {
             Message p = mMessages;
@@ -1656,12 +1625,10 @@
         }
     }
 
-    @NeverInline
     private void removeMessagesConcurrent(Handler h, int what, Object object) {
         findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true);
     }
 
-    @NeverInline
     private void removeMessagesLegacy(Handler h, int what, Object object) {
         synchronized (this) {
             Message p = mMessages;
@@ -1716,12 +1683,10 @@
         }
     }
 
-    @NeverInline
     private void removeEqualMessagesConcurrent(Handler h, int what, Object object) {
             findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true);
     }
 
-    @NeverInline
     private void removeEqualMessagesLegacy(Handler h, int what, Object object) {
         synchronized (this) {
             Message p = mMessages;
@@ -1777,12 +1742,10 @@
         }
     }
 
-    @NeverInline
     private void removeMessagesConcurrent(Handler h, Runnable r, Object object) {
         findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
     }
 
-    @NeverInline
     private void removeMessagesLegacy(Handler h, Runnable r, Object object) {
         synchronized (this) {
             Message p = mMessages;
@@ -1852,12 +1815,10 @@
     private final MatchHandlerRunnableAndObjectEquals mMatchHandlerRunnableAndObjectEquals =
             new MatchHandlerRunnableAndObjectEquals();
 
-    @NeverInline
     private void removeEqualMessagesConcurrent(Handler h, Runnable r, Object object) {
         findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
     }
 
-    @NeverInline
     private void removeEqualMessagesLegacy(Handler h, Runnable r, Object object) {
         synchronized (this) {
             Message p = mMessages;
@@ -1926,12 +1887,10 @@
     }
     private final MatchHandlerAndObject mMatchHandlerAndObject = new MatchHandlerAndObject();
 
-    @NeverInline
     private void removeCallbacksAndMessagesConcurrent(Handler h, Object object) {
             findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true);
     }
 
-    @NeverInline
     private void removeCallbacksAndMessagesLegacy(Handler h, Object object) {
         synchronized (this) {
             Message p = mMessages;
@@ -2000,12 +1959,10 @@
     private final MatchHandlerAndObjectEquals mMatchHandlerAndObjectEquals =
             new MatchHandlerAndObjectEquals();
 
-    @NeverInline
     void removeCallbacksAndEqualMessagesConcurrent(Handler h, Object object) {
         findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
     }
 
-    @NeverInline
     void removeCallbacksAndEqualMessagesLegacy(Handler h, Object object) {
         synchronized (this) {
             Message p = mMessages;
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index 80da487..7e0995c 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -588,7 +588,7 @@
     private static final AtomicLong mMessagesDelivered = new AtomicLong();
     private boolean mMessageDirectlyQueued;
 
-    private Message nextMessage(boolean peek) {
+    private Message nextMessage(boolean peek, boolean returnEarliest) {
         int i = 0;
 
         while (true) {
@@ -665,7 +665,7 @@
              * If we have a barrier we should return the async node (if it exists and is ready)
              */
             if (msgNode != null && msgNode.isBarrier()) {
-                if (asyncMsgNode != null && now >= asyncMsgNode.getWhen()) {
+                if (asyncMsgNode != null && (returnEarliest || now >= asyncMsgNode.getWhen())) {
                     found = asyncMsgNode;
                 } else {
                     next = asyncMsgNode;
@@ -679,7 +679,7 @@
                 earliest = pickEarliestNode(msgNode, asyncMsgNode);
 
                 if (earliest != null) {
-                    if (now >= earliest.getWhen()) {
+                    if (returnEarliest || now >= earliest.getWhen()) {
                         found = earliest;
                     } else {
                         next = earliest;
@@ -784,7 +784,7 @@
             mMessageDirectlyQueued = false;
             nativePollOnce(ptr, mNextPollTimeoutMillis);
 
-            Message msg = nextMessage(false);
+            Message msg = nextMessage(false, false);
             if (msg != null) {
                 msg.markInUse();
                 return msg;
@@ -1089,7 +1089,7 @@
      */
     Long peekWhenForTest() {
         throwIfNotTest();
-        Message ret = nextMessage(true);
+        Message ret = nextMessage(true, true);
         return ret != null ? ret.when : null;
     }
 
@@ -1102,7 +1102,7 @@
     @Nullable
     Message pollForTest() {
         throwIfNotTest();
-        return nextMessage(false);
+        return nextMessage(false, true);
     }
 
     /**
@@ -1116,7 +1116,7 @@
         throwIfNotTest();
 
         // Call nextMessage to get the stack drained into our priority queues
-        nextMessage(true);
+        nextMessage(true, false);
 
         Iterator<MessageNode> queueIter = mPriorityQueue.iterator();
         MessageNode queueNode = iterateNext(queueIter);
diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
index cde2ba56..132bdd1 100644
--- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
@@ -759,27 +759,27 @@
                 if (now >= msg.when) {
                     // Got a message.
                     mBlocked = false;
-                    if (prevMsg != null) {
-                        prevMsg.next = msg.next;
-                        if (prevMsg.next == null) {
-                            mLast = prevMsg;
-                        }
-                    } else {
-                        mMessages = msg.next;
-                        if (msg.next == null) {
-                            mLast = null;
-                        }
-                    }
-                    msg.next = null;
-                    msg.markInUse();
-                    if (msg.isAsynchronous()) {
-                        mAsyncMessageCount--;
-                    }
-                    if (TRACE) {
-                        Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
-                    }
-                    return msg;
                 }
+                if (prevMsg != null) {
+                    prevMsg.next = msg.next;
+                    if (prevMsg.next == null) {
+                        mLast = prevMsg;
+                    }
+                } else {
+                    mMessages = msg.next;
+                    if (msg.next == null) {
+                        mLast = null;
+                    }
+                }
+                msg.next = null;
+                msg.markInUse();
+                if (msg.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
+                if (TRACE) {
+                    Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+                }
+                return msg;
             }
         }
         return null;
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
index e3f251e..68f1570 100644
--- a/core/java/android/os/PerfettoTrace.java
+++ b/core/java/android/os/PerfettoTrace.java
@@ -154,14 +154,44 @@
         }
     }
 
+    /**
+     * Manages a perfetto tracing session.
+     * Constructing this object with a config automatically starts a tracing session. Each session
+     * must be closed after use and then the resulting trace bytes can be read.
+     *
+     * The session could be in process or system wide, depending on {@code isBackendInProcess}.
+     * This functionality is intended for testing.
+     */
+    public static final class Session {
+        private final long mPtr;
+
+        /**
+         * Session ctor.
+         */
+        public Session(boolean isBackendInProcess, byte[] config) {
+            mPtr = native_start_session(isBackendInProcess, config);
+        }
+
+        /**
+         * Closes the session and returns the trace.
+         */
+        public byte[] close() {
+            return native_stop_session(mPtr);
+        }
+    }
+
     @CriticalNative
     private static native long native_get_process_track_uuid();
-
     @CriticalNative
     private static native long native_get_thread_track_uuid(long tid);
 
     @FastNative
     private static native void native_activate_trigger(String name, int ttlMs);
+    @FastNative
+    private static native void native_register(boolean isBackendInProcess);
+
+    private static native long native_start_session(boolean isBackendInProcess, byte[] config);
+    private static native byte[] native_stop_session(long ptr);
 
     /**
      * Writes a trace message to indicate a given section of code was invoked.
@@ -307,7 +337,7 @@
     /**
      * Registers the process with Perfetto.
      */
-    public static void register() {
-        Trace.registerWithPerfetto();
+    public static void register(boolean isBackendInProcess) {
+        native_register(isBackendInProcess);
     }
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2a5666c..e769abe 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -624,6 +624,7 @@
             WAKE_REASON_TAP,
             WAKE_REASON_LIFT,
             WAKE_REASON_BIOMETRIC,
+            WAKE_REASON_DOCK,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface WakeReason{}
@@ -765,6 +766,12 @@
     public static final int WAKE_REASON_BIOMETRIC = 17;
 
     /**
+     * Wake up reason code: Waking up due to a user docking the device.
+     * @hide
+     */
+    public static final int WAKE_REASON_DOCK = 18;
+
+    /**
      * Convert the wake reason to a string for debugging purposes.
      * @hide
      */
@@ -788,6 +795,7 @@
             case WAKE_REASON_TAP: return "WAKE_REASON_TAP";
             case WAKE_REASON_LIFT: return "WAKE_REASON_LIFT";
             case WAKE_REASON_BIOMETRIC: return "WAKE_REASON_BIOMETRIC";
+            case WAKE_REASON_DOCK: return "WAKE_REASON_DOCK";
             default: return Integer.toString(wakeReason);
         }
     }
diff --git a/core/java/android/os/TestLooperManager.java b/core/java/android/os/TestLooperManager.java
index d451109..ddfa379 100644
--- a/core/java/android/os/TestLooperManager.java
+++ b/core/java/android/os/TestLooperManager.java
@@ -84,17 +84,8 @@
      * interactions with it have completed.
      */
     public Message next() {
-        // Wait for the looper block to come up, to make sure we don't accidentally get
-        // the message for the block.
-        while (!mLooperIsMyLooper && !mLooperBlocked) {
-            synchronized (this) {
-                try {
-                    wait();
-                } catch (InterruptedException e) {
-                }
-            }
-        }
         checkReleased();
+        waitForLooperHolder();
         return mQueue.next();
     }
 
@@ -110,6 +101,7 @@
     @Nullable
     public Message poll() {
         checkReleased();
+        waitForLooperHolder();
         return mQueue.pollForTest();
     }
 
@@ -124,6 +116,7 @@
     @Nullable
     public Long peekWhen() {
         checkReleased();
+        waitForLooperHolder();
         return mQueue.peekWhenForTest();
     }
 
@@ -133,6 +126,7 @@
     @FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
     public boolean isBlockedOnSyncBarrier() {
         checkReleased();
+        waitForLooperHolder();
         return mQueue.isBlockedOnSyncBarrier();
     }
 
@@ -221,6 +215,23 @@
         }
     }
 
+    /**
+     * Waits until the Looper is blocked by the LooperHolder, if one was posted.
+     *
+     * After this method returns, it's guaranteed that the LooperHolder Message
+     * is not in the underlying queue.
+     */
+    private void waitForLooperHolder() {
+        while (!mLooperIsMyLooper && !mLooperBlocked) {
+            synchronized (this) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
     private class LooperHolder implements Runnable {
         @Override
         public void run() {
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 4a37e0a..09e6a45 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -164,8 +164,6 @@
     private static native void nativeInstant(long tag, String name);
     @FastNative
     private static native void nativeInstantForTrack(long tag, String trackName, String name);
-    @FastNative
-    private static native void nativeRegisterWithPerfetto();
 
     private Trace() {
     }
@@ -545,6 +543,6 @@
      * @hide
      */
     public static void registerWithPerfetto() {
-        nativeRegisterWithPerfetto();
+        PerfettoTrace.register(false /* isBackendInProcess */);
     }
 }
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 976bfe4..62d5015 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -147,7 +147,7 @@
 
                 for (int screenState = 0; screenState < SCREEN_STATE_COUNT; screenState++) {
                     if (mData.layout.screenStateDataIncluded
-                            && screenState == POWER_STATE_UNSPECIFIED) {
+                            && screenState == SCREEN_STATE_UNSPECIFIED) {
                         continue;
                     }
 
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 967f55c..aaf6489 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2910,10 +2910,18 @@
      * <p>Currently, on most form factors the first human user on the device will be the main user;
      * in the future, the concept may be transferable, so a different user (or even no user at all)
      * may be designated the main user instead. On other form factors there might not be a main
+     * user. In the future, the concept may be removed, i.e. typical future devices may have no main
      * user.
      *
      * <p>Note that this will not be the system user on devices for which
      * {@link #isHeadlessSystemUserMode()} returns true.
+     *
+     * <p>NB: Features should ideally not limit functionality to the main user. Ideally, they
+     * should either work for all users or for all admin users. If a feature should only work for
+     * select users, its determination of which user should be done intelligently or be
+     * customizable. Not all devices support a main user, and the idea of singling out one user as
+     * special is contrary to overall multiuser goals.
+     *
      * @hide
      */
     @SystemApi
@@ -2930,6 +2938,12 @@
     /**
      * Returns the designated "main user" of the device, or {@code null} if there is no main user.
      *
+     * <p>NB: Features should ideally not limit functionality to the main user. Ideally, they
+     * should either work for all users or for all admin users. If a feature should only work for
+     * select users, its determination of which user should be done intelligently or be
+     * customizable. Not all devices support a main user, and the idea of singling out one user as
+     * special is contrary to overall multiuser goals.
+     *
      * @see #isMainUser()
      * @hide
      */
@@ -3782,9 +3796,9 @@
     @UnsupportedAppUsage
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
-    @CachedProperty(modsFlagOnOrNone = {}, api = "is_user_unlocked")
+    @CachedProperty(api = "is_user_unlocked")
     public boolean isUserUnlocked(@UserIdInt int userId) {
-        return ((UserManagerCache) mIpcDataCache).isUserUnlocked(mService::isUserUnlocked, userId);
+        return UserManagerCache.isUserUnlocked(mService::isUserUnlocked, userId);
     }
 
     /** @hide */
@@ -3820,9 +3834,9 @@
     /** @hide */
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
-    @CachedProperty(modsFlagOnOrNone = {}, api = "is_user_unlocked")
+    @CachedProperty(api = "is_user_unlocked")
     public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
-        return ((UserManagerCache) mIpcDataCache)
+        return UserManagerCache
                 .isUserUnlockingOrUnlocked(mService::isUserUnlockingOrUnlocked, userId);
     }
 
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index b12433a7..9e7c9f6 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -198,6 +198,13 @@
 }
 
 flag {
+     name: "disable_madvise_artfile_default"
+     namespace: "system_performance"
+     description: "Disables madvise of .art files by default during app start."
+     bug: "382110550"
+}
+
+flag {
     name: "disallow_cellular_null_ciphers_restriction"
     namespace: "cellular_security"
     description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices."
diff --git a/core/java/android/service/contextualsearch/OWNERS b/core/java/android/service/contextualsearch/OWNERS
index c435bd8..af2ed4d 100644
--- a/core/java/android/service/contextualsearch/OWNERS
+++ b/core/java/android/service/contextualsearch/OWNERS
@@ -1,3 +1,4 @@
 srazdan@google.com
 hyunyoungs@google.com
 awickham@google.com
+rgl@google.com
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 073f512..1f1427d 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.StringDef;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Notification;
 import android.os.Build;
 import android.os.Bundle;
@@ -65,7 +66,8 @@
             KEY_SENSITIVE_CONTENT,
             KEY_RANKING_SCORE,
             KEY_NOT_CONVERSATION,
-            KEY_TYPE
+            KEY_TYPE,
+            KEY_SUMMARIZATION
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Keys {}
@@ -188,7 +190,7 @@
             TYPE_PROMOTION,
             TYPE_SOCIAL_MEDIA,
             TYPE_NEWS,
-            TYPE_CONTENT_RECOMMENDATION
+            TYPE_CONTENT_RECOMMENDATION,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Types {}
@@ -223,6 +225,14 @@
     public static final int TYPE_CONTENT_RECOMMENDATION = 4;
 
     /**
+     * Data type: String, the classification type of this notification. The OS may display
+     * notifications differently depending on the type, and may change the alerting level of the
+     * notification.
+     */
+    @FlaggedApi(android.app.Flags.FLAG_NM_SUMMARIZATION)
+    public static final String KEY_SUMMARIZATION = "key_summarization";
+
+    /**
      * Create a notification adjustment.
      *
      * @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 0a9276c..0372926 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -113,6 +113,8 @@
      * <p>
      * Input: {@link #EXTRA_NOTIFICATION_KEY}, the {@link StatusBarNotification#getKey()} of the
      * notification the user wants to file feedback for.
+     * Input: {@link #EXTRA_NOTIFICATION_ADJUSTMENT}, the {@link Adjustment} key that the user wants
+     * to file feedback about.
      * <p>
      * Output: Nothing.
      */
@@ -131,6 +133,16 @@
             = "android.service.notification.extra.NOTIFICATION_KEY";
 
     /**
+     * A string extra containing the {@link Adjustment} key that the user wants to file feedback
+     * about.
+     *
+     * Extra for {@link #ACTION_NOTIFICATION_ASSISTANT_FEEDBACK_SETTINGS}.
+     */
+    @FlaggedApi(android.app.Flags.FLAG_NM_SUMMARIZATION)
+    public static final String EXTRA_NOTIFICATION_ADJUSTMENT
+            = "android.service.notification.extra.NOTIFICATION_ADJUSTMENT";
+
+    /**
      * Data type: int, the feedback rating score provided by user. The score can be any integer
      *            value depends on the experimental and feedback UX design.
      */
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 7256907..f230065 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UiThread;
 import android.app.ActivityManager;
 import android.app.INotificationManager;
@@ -56,6 +57,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.widget.RemoteViews;
@@ -1824,6 +1826,7 @@
         private int mProposedImportance;
         // Sensitive info detected by the notification assistant
         private boolean mSensitiveContent;
+        private String mSummarization;
 
         private static final int PARCEL_VERSION = 2;
 
@@ -1864,6 +1867,7 @@
             out.writeBoolean(mIsBubble);
             out.writeInt(mProposedImportance);
             out.writeBoolean(mSensitiveContent);
+            out.writeString(mSummarization);
         }
 
         /** @hide */
@@ -1904,6 +1908,7 @@
             mIsBubble = in.readBoolean();
             mProposedImportance = in.readInt();
             mSensitiveContent = in.readBoolean();
+            mSummarization = in.readString();
         }
 
 
@@ -2180,6 +2185,16 @@
         }
 
         /**
+         * Returns a summary of the content in the notification, or potentially of the current
+         * notification and related notifications (for example, if this is provided for a group
+         * summary notification it may be summarizing all the child notifications).
+         */
+        @FlaggedApi(android.app.Flags.FLAG_NM_SUMMARIZATION)
+        public @Nullable String getSummarization() {
+            return mSummarization;
+        }
+
+        /**
          * Returns the intended transition to ranking passed by {@link NotificationAssistantService}
          * @hide
          */
@@ -2201,7 +2216,7 @@
                 ArrayList<CharSequence> smartReplies, boolean canBubble,
                 boolean isTextChanged, boolean isConversation, ShortcutInfo shortcutInfo,
                 int rankingAdjustment, boolean isBubble, int proposedImportance,
-                boolean sensitiveContent) {
+                boolean sensitiveContent, String summarization) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -2229,6 +2244,7 @@
             mIsBubble = isBubble;
             mProposedImportance = proposedImportance;
             mSensitiveContent = sensitiveContent;
+            mSummarization = TextUtils.nullIfEmpty(summarization);
         }
 
         /**
@@ -2271,7 +2287,8 @@
                     other.mRankingAdjustment,
                     other.mIsBubble,
                     other.mProposedImportance,
-                    other.mSensitiveContent);
+                    other.mSensitiveContent,
+                    other.mSummarization);
         }
 
         /**
@@ -2332,7 +2349,8 @@
                     && Objects.equals(mRankingAdjustment, other.mRankingAdjustment)
                     && Objects.equals(mIsBubble, other.mIsBubble)
                     && Objects.equals(mProposedImportance, other.mProposedImportance)
-                    && Objects.equals(mSensitiveContent, other.mSensitiveContent);
+                    && Objects.equals(mSensitiveContent, other.mSensitiveContent)
+                    && Objects.equals(mSummarization, other.mSummarization);
         }
     }
 
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 105fa3f..79957f4 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -18,6 +18,8 @@
 
 import static android.text.TextUtils.formatSimple;
 
+import static com.android.window.flags.Flags.enablePerDisplayPackageContextCacheInStatusbarNotif;
+
 import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -37,9 +39,9 @@
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import static com.android.window.flags.Flags.enablePerDisplayPackageContextCacheInStatusbarNotif;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Map;
 
 /**
@@ -81,7 +83,8 @@
     @Deprecated
     private Context mContext; // used for inflation & icon expansion
     // Maps display id to context used for remote view content inflation and status bar icon.
-    private final Map<Integer, Context> mContextForDisplayId = new ArrayMap<>();
+    private final Map<Integer, Context> mContextForDisplayId =
+            Collections.synchronizedMap(new ArrayMap<>());
 
     /** @hide */
     public StatusBarNotification(String pkg, String opPkg, int id,
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 1771642..6ed8c6d 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -76,22 +76,30 @@
     private IQuickAccessWalletService mService;
 
     @Nullable
-    private final QuickAccessWalletServiceInfo mServiceInfo;
+    private QuickAccessWalletServiceInfo mServiceInfo;
 
     private static final int MSG_TIMEOUT_SERVICE = 5;
 
     QuickAccessWalletClientImpl(@NonNull Context context, @Nullable Executor bgExecutor) {
         mContext = context.getApplicationContext();
-        mServiceInfo = QuickAccessWalletServiceInfo.tryCreate(context);
+        mServiceInfo = null;
         mHandler = new Handler(Looper.getMainLooper());
         mLifecycleExecutor = (bgExecutor == null) ? Runnable::run : bgExecutor;
         mRequestQueue = new ArrayDeque<>();
         mEventListeners = new HashMap<>(1);
     }
 
+    private QuickAccessWalletServiceInfo ensureServiceInfo() {
+        if (mServiceInfo == null) {
+            mServiceInfo = QuickAccessWalletServiceInfo.tryCreate(mContext);
+        }
+
+        return mServiceInfo;
+    }
+
     @Override
     public boolean isWalletServiceAvailable() {
-        return mServiceInfo != null;
+        return ensureServiceInfo() != null;
     }
 
     @Override
@@ -239,12 +247,13 @@
     @Override
     @Nullable
     public Intent createWalletIntent() {
-        if (mServiceInfo == null) {
+        QuickAccessWalletServiceInfo serviceInfo = ensureServiceInfo();
+        if (serviceInfo == null) {
             return null;
         }
-        String packageName = mServiceInfo.getComponentName().getPackageName();
-        int userId = mServiceInfo.getUserId();
-        String walletActivity = mServiceInfo.getWalletActivity();
+        String packageName = serviceInfo.getComponentName().getPackageName();
+        int userId = serviceInfo.getUserId();
+        String walletActivity = serviceInfo.getWalletActivity();
         return createIntent(walletActivity, packageName, userId, ACTION_VIEW_WALLET);
     }
 
@@ -298,11 +307,12 @@
     @Override
     @Nullable
     public Intent createWalletSettingsIntent() {
-        if (mServiceInfo == null) {
+        QuickAccessWalletServiceInfo serviceInfo = ensureServiceInfo();
+        if (serviceInfo == null) {
             return null;
         }
-        String packageName = mServiceInfo.getComponentName().getPackageName();
-        String settingsActivity = mServiceInfo.getSettingsActivity();
+        String packageName = serviceInfo.getComponentName().getPackageName();
+        String settingsActivity = serviceInfo.getSettingsActivity();
         return createIntent(settingsActivity, packageName, UserHandle.myUserId(),
                 ACTION_VIEW_WALLET_SETTINGS);
     }
@@ -356,36 +366,42 @@
     @Override
     @Nullable
     public Drawable getLogo() {
-        return mServiceInfo == null ? null : mServiceInfo.getWalletLogo(mContext);
+        QuickAccessWalletServiceInfo serviceInfo = ensureServiceInfo();
+        return serviceInfo == null ? null : serviceInfo.getWalletLogo(mContext);
     }
 
     @Nullable
     @Override
     public Drawable getTileIcon() {
-        return mServiceInfo == null ? null : mServiceInfo.getTileIcon();
+        QuickAccessWalletServiceInfo serviceInfo = ensureServiceInfo();
+        return serviceInfo == null ? null : serviceInfo.getTileIcon();
     }
 
     @Nullable
     @Override
     public UserHandle getUser() {
-        return mServiceInfo == null ? null : UserHandle.of(mServiceInfo.getUserId());
+        QuickAccessWalletServiceInfo serviceInfo = ensureServiceInfo();
+        return serviceInfo == null ? null : UserHandle.of(serviceInfo.getUserId());
     }
 
     @Override
     @Nullable
     public CharSequence getServiceLabel() {
-        return mServiceInfo == null ? null : mServiceInfo.getServiceLabel(mContext);
+        QuickAccessWalletServiceInfo serviceInfo = ensureServiceInfo();
+        return serviceInfo == null ? null : serviceInfo.getServiceLabel(mContext);
     }
 
     @Override
     @Nullable
     public CharSequence getShortcutShortLabel() {
-        return mServiceInfo == null ? null : mServiceInfo.getShortcutShortLabel(mContext);
+        QuickAccessWalletServiceInfo serviceInfo = ensureServiceInfo();
+        return serviceInfo == null ? null : serviceInfo.getShortcutShortLabel(mContext);
     }
 
     @Override
     public CharSequence getShortcutLongLabel() {
-        return mServiceInfo == null ? null : mServiceInfo.getShortcutLongLabel(mContext);
+        QuickAccessWalletServiceInfo serviceInfo = ensureServiceInfo();
+        return serviceInfo == null ? null : serviceInfo.getShortcutLongLabel(mContext);
     }
 
     private void connect() {
@@ -393,7 +409,8 @@
     }
 
     private void connectInternal() {
-        if (mServiceInfo == null) {
+        QuickAccessWalletServiceInfo serviceInfo = ensureServiceInfo();
+        if (serviceInfo == null) {
             Log.w(TAG, "Wallet service unavailable");
             return;
         }
@@ -402,7 +419,7 @@
         }
         mIsConnected = true;
         Intent intent = new Intent(SERVICE_INTERFACE);
-        intent.setComponent(mServiceInfo.getComponentName());
+        intent.setComponent(serviceInfo.getComponentName());
         int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
         mLifecycleExecutor.execute(() -> mContext.bindService(intent, this, flags));
         resetServiceConnectionTimeout();
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index cb49850..a5d52957 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -121,6 +121,7 @@
             b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
             b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
             b.mLineBreakConfig = LineBreakConfig.NONE;
+            b.mUseBoundsForWidth = false;
             b.mMinimumFontMetrics = null;
             return b;
         }
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 053ccdd..7c1e497 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -372,8 +372,10 @@
 
     /**
      * @hide
+     * @deprecated Use vsync IDs with the regular Choreographer instead.
      */
     @UnsupportedAppUsage
+    @Deprecated
     public static Choreographer getSfInstance() {
         return sSfThreadInstance.get();
     }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 2ec5dbc..f58baff 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -1146,14 +1146,6 @@
      */
     KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId);
 
-    /*
-     * Notifies about IME insets animation.
-     *
-     * @param running Indicates the insets animation state.
-     * @param animationType Indicates the {@link InsetsController.AnimationType}
-     */
-     oneway void notifyImeInsetsAnimationStateChanged(boolean running, int animationType);
-
     /**
      * Returns whether the display with {@code displayId} ignores orientation request.
      */
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index e097a07..c174fbe 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -214,14 +214,9 @@
          * Notifies when the state of running animation is changed. The state is either "running" or
          * "idle".
          *
-         * @param running       {@code true} if the given insets types start running
-         *                      {@code false} otherwise.
-         * @param animationType {@link AnimationType}
-         * @param insetsTypes   {@link Type}.
+         * @param running {@code true} if there is any animation running; {@code false} otherwise.
          */
-        default void notifyAnimationRunningStateChanged(boolean running,
-                @AnimationType int animationType, @InsetsType int insetsTypes) {
-        }
+        default void notifyAnimationRunningStateChanged(boolean running) {}
 
         /** @see ViewRootImpl#isHandlingPointerEvent */
         default boolean isHandlingPointerEvent() {
@@ -749,8 +744,9 @@
                     final InsetsAnimationControlRunner runner = new InsetsResizeAnimationRunner(
                             mFrame, mFromState, mToState, RESIZE_INTERPOLATOR,
                             ANIMATION_DURATION_RESIZE, mTypes, InsetsController.this);
-                    mHost.notifyAnimationRunningStateChanged(true,
-                            runner.getAnimationType(), mTypes);
+                    if (mRunningAnimations.isEmpty()) {
+                        mHost.notifyAnimationRunningStateChanged(true);
+                    }
                     mRunningAnimations.add(new RunningAnimation(runner, runner.getAnimationType()));
                 }
             };
@@ -1564,7 +1560,9 @@
             }
         }
         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_ANIMATION_RUNNING);
-        mHost.notifyAnimationRunningStateChanged(true, animationType, types);
+        if (mRunningAnimations.isEmpty()) {
+            mHost.notifyAnimationRunningStateChanged(true);
+        }
         mRunningAnimations.add(new RunningAnimation(runner, animationType));
         if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
                 + useInsetsAnimationThread);
@@ -1844,8 +1842,9 @@
                 break;
             }
         }
-        mHost.notifyAnimationRunningStateChanged(
-                false, control.getAnimationType(), removedTypes);
+        if (mRunningAnimations.isEmpty()) {
+            mHost.notifyAnimationRunningStateChanged(false);
+        }
         onAnimationStateChanged(removedTypes, false /* running */);
     }
 
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index bd6ff4c..ae0e9c6 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -532,14 +532,30 @@
     public static final int FLAG_NO_FOCUS_CHANGE = MotionEventFlag.NO_FOCUS_CHANGE;
 
     /**
-     * This flag indicates that this event was modified by or generated from an accessibility
-     * service. Value = 0x800
+     * This flag indicates that this event was injected from some
+     * {@link android.accessibilityservice.AccessibilityService}, which may be either an
+     * Accessibility Tool OR a service using that API for purposes other than assisting users with
+     * disabilities. Value = 0x800
+     * @see #FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL
      * @hide
      */
     @TestApi
     public static final int FLAG_IS_ACCESSIBILITY_EVENT = MotionEventFlag.IS_ACCESSIBILITY_EVENT;
 
     /**
+     * This flag indicates that this event was injected from an
+     * {@link android.accessibilityservice.AccessibilityService} with the
+     * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool()} property
+     * set to true. These services (known as "Accessibility Tools") are used to assist users with
+     * disabilities, so events from these services should be able to reach all Views including
+     * Views which set {@link View#isAccessibilityDataSensitive()} to true.
+     * Value = 0x1000
+     * @hide
+     */
+    public static final int FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL =
+            MotionEventFlag.INJECTED_FROM_ACCESSIBILITY_TOOL;
+
+    /**
      * Private flag that indicates when the system has detected that this motion event
      * may be inconsistent with respect to the sequence of previously delivered motion events,
      * such as when a pointer move event is sent but the pointer is not down.
@@ -2534,6 +2550,24 @@
                 : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS);
     }
 
+    /**
+     * @see #FLAG_IS_ACCESSIBILITY_EVENT
+     * @hide
+     */
+    public boolean isInjectedFromAccessibilityService() {
+        final int flags = getFlags();
+        return (flags & FLAG_IS_ACCESSIBILITY_EVENT) != 0;
+    }
+
+    /**
+     * @see #FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL
+     * @hide
+     */
+    public boolean isInjectedFromAccessibilityTool() {
+        final int flags = getFlags();
+        return (flags & FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL) != 0;
+    }
+
     /** @hide */
     public final boolean isHoverExitPending() {
         final int flags = getFlags();
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 3a0e6f1..73cd5ec 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.app.Flags.notificationsRedesignTemplates;
+import static android.util.MathUtils.abs;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
@@ -31,6 +32,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.widget.FrameLayout;
 import android.widget.RelativeLayout;
 import android.widget.RemoteViews;
@@ -60,6 +62,8 @@
     private boolean mEntireHeaderClickable;
     private boolean mExpandOnlyOnButton;
     private boolean mAcceptAllTouches;
+    private float mTopLineTranslation;
+    private float mExpandButtonTranslation;
 
     ViewOutlineProvider mProvider = new ViewOutlineProvider() {
         @Override
@@ -205,6 +209,52 @@
         mExpandButton.setLayoutParams(lp);
     }
 
+    /** The view containing the app name, timestamp etc at the top of the notification. */
+    public NotificationTopLineView getTopLineView() {
+        return mTopLineView;
+    }
+
+    /** The view containing the button to expand the notification. */
+    public NotificationExpandButton getExpandButton() {
+        return mExpandButton;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        if (notificationsRedesignTemplates()) {
+            mTopLineTranslation = measureCenterTranslation(mTopLineView);
+            mExpandButtonTranslation = measureCenterTranslation(mExpandButton);
+        }
+    }
+
+    private float measureCenterTranslation(View view) {
+        // When the view is centered (see centerTopLine), its height is MATCH_PARENT
+        int parentHeight = getMeasuredHeight();
+        // When the view is top-aligned, its height is WRAP_CONTENT
+        float wrapContentHeight = view.getMeasuredHeight();
+        // Calculate the translation needed between the two alignments
+        final MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
+        return abs((parentHeight - wrapContentHeight) / 2f - lp.topMargin);
+    }
+
+    /**
+     * The vertical translation necessary between the two positions of the top line, to be used in
+     * the animation. See also {@link NotificationHeaderView#centerTopLine(boolean)}.
+     */
+    public float getTopLineTranslation() {
+        return mTopLineTranslation;
+    }
+
+    /**
+     * The vertical translation necessary between the two positions of the expander, to be used in
+     * the animation. See also {@link NotificationHeaderView#centerTopLine(boolean)}.
+     */
+    public float getExpandButtonTranslation() {
+        return mExpandButtonTranslation;
+    }
+
     /**
      * This is used to make the low-priority header show the bolded text of a title.
      *
@@ -216,14 +266,20 @@
                 ? R.style.TextAppearance_DeviceDefault_Notification_Title
                 : R.style.TextAppearance_DeviceDefault_Notification_Info;
         // Most of the time, we're showing text in the minimized state
-        View headerText = findViewById(R.id.header_text);
-        if (headerText instanceof TextView) {
-            ((TextView) headerText).setTextAppearance(styleResId);
+        if (findViewById(R.id.header_text) instanceof TextView headerText) {
+            headerText.setTextAppearance(styleResId);
+            if (notificationsRedesignTemplates()) {
+                // TODO: b/378660052 - When inlining the redesign flag, this should be updated
+                //  directly in TextAppearance_DeviceDefault_Notification_Title so we won't need to
+                //  override it here.
+                float textSize = getContext().getResources().getDimension(
+                        R.dimen.notification_2025_title_text_size);
+                headerText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+            }
         }
         // If there's no summary or text, we show the app name instead of nothing
-        View appNameText = findViewById(R.id.app_name_text);
-        if (appNameText instanceof TextView) {
-            ((TextView) appNameText).setTextAppearance(styleResId);
+        if (findViewById(R.id.app_name_text) instanceof TextView appNameText) {
+            appNameText.setTextAppearance(styleResId);
         }
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7206906..0866e0d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16654,6 +16654,16 @@
             // Window is obscured, drop this touch.
             return false;
         }
+        if (android.view.accessibility.Flags.preventA11yNontoolFromInjectingIntoSensitiveViews()) {
+            if (event.isInjectedFromAccessibilityService()
+                    // If the event came from an Accessibility Service that does *not* declare
+                    // itself as AccessibilityServiceInfo#isAccessibilityTool and this View is
+                    // declared sensitive then drop the event.
+                    // Only Accessibility Tools are allowed to interact with sensitive Views.
+                    && !event.isInjectedFromAccessibilityTool() && isAccessibilityDataSensitive()) {
+                return false;
+            }
+        }
         return true;
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7c5b300..bf34069 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -24,7 +24,6 @@
 import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
 import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
 import static android.os.Trace.TRACE_TAG_VIEW;
-import static android.service.autofill.Flags.improveFillDialogAconfig;
 import static android.util.SequenceUtils.getInitSeq;
 import static android.util.SequenceUtils.isIncomingSeqStale;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -923,8 +922,6 @@
 
     private boolean mInsetsAnimationRunning;
 
-    private int mInsetsAnimatingTypes = 0;
-
     private long mPreviousFrameDrawnTime = -1;
     // The largest view size percentage to the display size. Used on trace to collect metric.
     private float mLargestChildPercentage = 0.0f;
@@ -2523,49 +2520,17 @@
      * Notify the when the running state of a insets animation changed.
      */
     @VisibleForTesting
-    public void notifyInsetsAnimationRunningStateChanged(boolean running,
-            @InsetsController.AnimationType int animationType,
-            @InsetsType int insetsTypes) {
-        @InsetsType int previousInsetsType = mInsetsAnimatingTypes;
-        // If improveFillDialogAconfig is disabled, we notify WindowSession of all the updates we
-        // receive here
-        boolean notifyWindowSession = !improveFillDialogAconfig();
-        if (running) {
-            mInsetsAnimatingTypes |= insetsTypes;
-        } else {
-            mInsetsAnimatingTypes &= ~insetsTypes;
-        }
+    public void notifyInsetsAnimationRunningStateChanged(boolean running) {
         if (sToolkitSetFrameRateReadOnlyFlagValue) {
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.instant(Trace.TRACE_TAG_VIEW,
+                        TextUtils.formatSimple("notifyInsetsAnimationRunningStateChanged(%s)",
+                        Boolean.toString(running)));
+            }
             mInsetsAnimationRunning = running;
-            // If improveFillDialogAconfig is enabled, we need to confirm other animations aren't
-            // running to maintain the existing behavior. System server were notified previously
-            // only when animation started running or stopped when there were no running animations.
-            if (improveFillDialogAconfig()) {
-                if ((previousInsetsType == 0 && mInsetsAnimatingTypes != 0)
-                        || (previousInsetsType != 0 && mInsetsAnimatingTypes == 0)) {
-                    notifyWindowSession = true;
-                }
-            }
-            if (notifyWindowSession) {
-                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-                    Trace.instant(Trace.TRACE_TAG_VIEW,
-                            TextUtils.formatSimple("notifyInsetsAnimationRunningStateChanged(%s)",
-                                    Boolean.toString(running)));
-                }
-                try {
-                    mWindowSession.notifyInsetsAnimationRunningStateChanged(mWindow, running);
-                } catch (RemoteException e) {
-                }
-            }
-        }
-        if (improveFillDialogAconfig()) {
-            // Update WindowManager for ImeAnimation
-            if ((insetsTypes & WindowInsets.Type.ime()) != 0) {
-                try {
-                    WindowManagerGlobal.getWindowManagerService()
-                            .notifyImeInsetsAnimationStateChanged(running, animationType);
-                } catch (RemoteException e) {
-                }
+            try {
+                mWindowSession.notifyInsetsAnimationRunningStateChanged(mWindow, running);
+            } catch (RemoteException e) {
             }
         }
     }
@@ -5601,9 +5566,6 @@
             if (mAttachInfo.mContentCaptureManager != null) {
                 ContentCaptureSession session =
                         mAttachInfo.mContentCaptureManager.getMainContentCaptureSession();
-                if (android.view.contentcapture.flags.Flags.postCreateAndroidBgThread()) {
-                    session.performStart();
-                }
                 session.notifyWindowBoundsChanged(session.getId(),
                         getConfiguration().windowConfiguration.getBounds());
             }
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index f1666db..889acca4 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -275,12 +275,9 @@
     }
 
     @Override
-    public void notifyAnimationRunningStateChanged(boolean running,
-            @InsetsController.AnimationType int animationType,
-            @WindowInsets.Type.InsetsType int insetsTypes) {
+    public void notifyAnimationRunningStateChanged(boolean running) {
         if (mViewRoot != null) {
-            mViewRoot.notifyInsetsAnimationRunningStateChanged(
-                    running, animationType, insetsTypes);
+            mViewRoot.notifyInsetsAnimationRunningStateChanged(running);
         }
     }
 
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 6d2c0d00..bb8958b 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.os.IInputConstants.POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY;
+import static android.os.IInputConstants.POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL;
 import static android.os.IInputConstants.POLICY_FLAG_KEY_GESTURE_TRIGGERED;
 
 import android.annotation.IntDef;
@@ -37,6 +38,7 @@
     int FLAG_VIRTUAL = 0x00000002;
 
     int FLAG_INJECTED_FROM_ACCESSIBILITY = POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY;
+    int FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL = POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL;
     int FLAG_KEY_GESTURE_TRIGGERED = POLICY_FLAG_KEY_GESTURE_TRIGGERED;
     int FLAG_INJECTED = 0x01000000;
     int FLAG_TRUSTED = 0x02000000;
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 294e5da..37f393e 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -4,6 +4,14 @@
 # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
 
 flag {
+    name: "a11y_character_in_window_api"
+    namespace: "accessibility"
+    description: "Enables new extra data key for an AccessibilityService to request character bounds in unmagnified window coordinates."
+    bug: "375429616"
+    is_exported: true
+}
+
+flag {
     name: "a11y_expansion_state_api"
     namespace: "accessibility"
     description: "Enables new APIs for an app to convey if a node is expanded or collapsed."
@@ -42,23 +50,15 @@
 }
 
 flag {
-    name: "a11y_character_in_window_api"
-    namespace: "accessibility"
-    description: "Enables new extra data key for an AccessibilityService to request character bounds in unmagnified window coordinates."
-    bug: "375429616"
-    is_exported: true
-}
-
-flag {
-    namespace: "accessibility"
     name: "allow_shortcut_chooser_on_lockscreen"
+    namespace: "accessibility"
     description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen"
     bug: "303871725"
 }
 
 flag {
-    namespace: "accessibility"
     name: "braille_display_hid"
+    namespace: "accessibility"
     is_exported: true
     description: "Enables new APIs for an AccessibilityService to communicate with a HID Braille display"
     bug: "303522222"
@@ -72,47 +72,62 @@
 }
 
 flag {
-    namespace: "accessibility"
     name: "collection_info_item_counts"
+    namespace: "accessibility"
     is_exported: true
     description: "Fields for total items and the number of important for accessibility items in a collection"
     bug: "302376158"
 }
 
 flag {
-    namespace: "accessibility"
     name: "copy_events_for_gesture_detection"
+    namespace: "accessibility"
     description: "Creates copies of MotionEvents and GestureEvents in GestureMatcher"
     bug: "280130713"
 }
 
 flag {
-    namespace: "accessibility"
     name: "deprecate_accessibility_announcement_apis"
+    namespace: "accessibility"
     description: "Controls the deprecation of platform APIs related to disruptive accessibility announcements"
     bug: "376727542"
     is_exported: true
 }
 
 flag {
-    namespace: "accessibility"
     name: "deprecate_ani_label_for_apis"
+    namespace: "accessibility"
     description: "Controls the deprecation of AccessibilityNodeInfo labelFor apis"
     bug: "333783827"
     is_exported: true
 }
 
 flag {
+    name: "enable_system_pinch_zoom_gesture"
     namespace: "accessibility"
+    description: "Feature flag for system pinch zoom gesture detector and related opt-out apis"
+    bug: "283323770"
+}
+
+flag {
+    name: "enable_type_window_control"
+    namespace: "accessibility"
+    is_exported: true
+    description: "adds new TYPE_WINDOW_CONTROL to AccessibilityWindowInfo for detecting Window Decorations"
+    bug: "320445550"
+}
+
+flag {
     name: "flash_notification_system_api"
+    namespace: "accessibility"
     is_exported: true
     description: "Makes flash notification APIs as system APIs for calling from mainline module"
     bug: "303131332"
 }
 
 flag {
-    namespace: "accessibility"
     name: "focus_rect_min_size"
+    namespace: "accessibility"
     description: "Ensures the a11y focus rect is big enough to be drawn as visible"
     bug: "368667566"
     metadata {
@@ -121,13 +136,45 @@
 }
 
 flag {
-    namespace: "accessibility"
     name: "force_invert_color"
+    namespace: "accessibility"
     description: "Enable force force-dark for smart inversion and dark theme everywhere"
     bug: "282821643"
 }
 
 flag {
+    name: "global_action_media_play_pause"
+    namespace: "accessibility"
+    description: "Allow AccessibilityService to perform GLOBAL_ACTION_MEDIA_PLAY_PAUSE"
+    bug: "334954140"
+    is_exported: true
+}
+
+flag {
+    name: "global_action_menu"
+    namespace: "accessibility"
+    description: "Allow AccessibilityService to perform GLOBAL_ACTION_MENU"
+    bug: "334954140"
+    is_exported: true
+}
+
+flag {
+    name: "granular_scrolling"
+    namespace: "accessibility"
+    is_exported: true
+    description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen"
+    bug: "302376158"
+}
+
+flag {
+    name: "indeterminate_range_info"
+    namespace: "accessibility"
+    description: "Creates a way to create an INDETERMINATE RangeInfo"
+    bug: "376108874"
+    is_exported: true
+}
+
+flag {
     name: "migrate_enable_shortcuts"
     namespace: "accessibility"
     description: "Refactors deprecated code to use AccessibilityManager#enableShortcutsForTargets."
@@ -146,70 +193,13 @@
 }
 
 flag {
+    name: "prevent_a11y_nontool_from_injecting_into_sensitive_views"
     namespace: "accessibility"
-    name: "global_action_menu"
-    description: "Allow AccessibilityService to perform GLOBAL_ACTION_MENU"
-    bug: "334954140"
-    is_exported: true
-}
-
-flag {
-    namespace: "accessibility"
-    name: "global_action_media_play_pause"
-    description: "Allow AccessibilityService to perform GLOBAL_ACTION_MEDIA_PLAY_PAUSE"
-    bug: "334954140"
-    is_exported: true
-}
-
-flag {
-    namespace: "accessibility"
-    name: "granular_scrolling"
-    is_exported: true
-    description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen"
-    bug: "302376158"
-}
-
-flag {
-    namespace: "accessibility"
-    name: "reduce_window_content_changed_event_throttle"
-    description: "Reduces the throttle of AccessibilityEvent of TYPE_WINDOW_CONTENT_CHANGED"
-    bug: "277305460"
-}
-
-flag {
-    namespace: "accessibility"
-    name: "remove_child_hover_check_for_touch_exploration"
-    description: "Remove a check for a hovered child that prevents touch events from being delegated to non-direct descendants"
-    bug: "304770837"
-}
-
-flag {
-    name: "skip_accessibility_warning_dialog_for_trusted_services"
-    namespace: "accessibility"
-    description: "Skips showing the accessibility warning dialog for trusted services."
-    bug: "303511250"
-}
-
-flag {
-    namespace: "accessibility"
-    name: "enable_type_window_control"
-    is_exported: true
-    description: "adds new TYPE_WINDOW_CONTROL to AccessibilityWindowInfo for detecting Window Decorations"
-    bug: "320445550"
-}
-
-flag {
-    namespace: "accessibility"
-    name: "update_always_on_a11y_service"
-    description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut."
-    bug: "298869916"
-}
-
-flag {
-    name: "enable_system_pinch_zoom_gesture"
-    namespace: "accessibility"
-    description: "Feature flag for system pinch zoom gesture detector and related opt-out apis"
-    bug: "283323770"
+    description: "Prevents injected gestures from A11yServices without isAccessibilityTool=true from reaching AccessibilityDataSensitive UI elements"
+    bug: "284180538"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
 }
 
 flag {
@@ -223,6 +213,30 @@
 }
 
 flag {
+    name: "reduce_window_content_changed_event_throttle"
+    namespace: "accessibility"
+    description: "Reduces the throttle of AccessibilityEvent of TYPE_WINDOW_CONTENT_CHANGED"
+    bug: "277305460"
+}
+
+flag {
+    name: "remove_child_hover_check_for_touch_exploration"
+    namespace: "accessibility"
+    description: "Remove a check for a hovered child that prevents touch events from being delegated to non-direct descendants"
+    bug: "304770837"
+}
+
+flag {
+    name: "restore_a11y_secure_settings_on_hsum_device"
+    namespace: "accessibility"
+    description: "Grab the a11y settings and send the settings restored broadcast for current visible foreground user"
+    bug: "381294327"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "restore_a11y_shortcut_target_service"
     namespace: "accessibility"
     description: "Perform merging and other bug fixes for SettingsProvider restore of ACCESSIBILITY_SHORTCUT_TARGET_SERVICES secure setting"
@@ -233,13 +247,10 @@
 }
 
 flag {
-    name: "restore_a11y_secure_settings_on_hsum_device"
+    name: "skip_accessibility_warning_dialog_for_trusted_services"
     namespace: "accessibility"
-    description: "Grab the a11y settings and send the settings restored broadcast for current visible foreground user"
-    bug: "381294327"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
+    description: "Skips showing the accessibility warning dialog for trusted services."
+    bug: "303511250"
 }
 
 flag {
@@ -274,6 +285,13 @@
 }
 
 flag {
+    namespace: "accessibility"
+    name: "update_always_on_a11y_service"
+    description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut."
+    bug: "298869916"
+}
+
+flag {
     name: "warning_use_default_dialog_type"
     namespace: "accessibility"
     description: "Uses the default type for the A11yService warning dialog, instead of SYSTEM_ALERT_DIALOG"
@@ -282,11 +300,3 @@
         purpose: PURPOSE_BUGFIX
     }
 }
-
-flag {
-    name: "indeterminate_range_info"
-    namespace: "accessibility"
-    description: "Creates a way to create an INDETERMINATE RangeInfo"
-    bug: "376108874"
-    is_exported: true
-}
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 6e2e100..8baa55f 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -74,8 +74,8 @@
     }
 
     @Override
-    void internalFlush(@FlushReason int reason) {
-        mParent.internalFlush(reason);
+    void flush(@FlushReason int reason) {
+        mParent.flush(reason);
     }
 
     @Override
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index b7a77d7..724e8fa 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -52,6 +52,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.RingBuffer;
 import com.android.internal.util.SyncResultReceiver;
 
@@ -604,6 +605,7 @@
                     mContext,
                     this,
                     prepareUiHandler(),
+                    prepareContentCaptureHandler(),
                     mService
                 );
                 if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
@@ -614,6 +616,15 @@
 
     @NonNull
     @GuardedBy("mLock")
+    private Handler prepareContentCaptureHandler() {
+        if (mContentCaptureHandler == null) {
+            mContentCaptureHandler = BackgroundThread.getHandler();
+        }
+        return mContentCaptureHandler;
+    }
+
+    @NonNull
+    @GuardedBy("mLock")
     private Handler prepareUiHandler() {
         if (mUiHandler == null) {
             mUiHandler = Handler.createAsync(Looper.getMainLooper());
@@ -663,7 +674,7 @@
     @UiThread
     public void flush(@FlushReason int reason) {
         if (mOptions.lite) return;
-        getMainContentCaptureSession().internalFlush(reason);
+        getMainContentCaptureSession().flush(reason);
     }
 
     /**
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 791a6f4..9aeec20 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -286,9 +286,6 @@
     abstract void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
             @NonNull ComponentName component, int flags);
 
-    /** @hide */
-    public void performStart() {}
-
     abstract boolean isDisabled();
 
     /**
@@ -342,7 +339,7 @@
     /**
      * Flushes the buffered events to the service.
      */
-    abstract void internalFlush(@FlushReason int reason);
+    abstract void flush(@FlushReason int reason);
 
     /**
      * Sets the {@link ContentCaptureContext} associated with the session.
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 29cae85..2fb78c0 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -57,12 +57,10 @@
 import android.view.ViewStructure;
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
-import android.view.contentcapture.flags.Flags;
 import android.view.contentprotection.ContentProtectionEventProcessor;
 import android.view.inputmethod.BaseInputConnection;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.IResultReceiver;
 import com.android.modules.expresslog.Counter;
 
@@ -109,10 +107,8 @@
     @NonNull
     private final Handler mUiHandler;
 
-    /** @hide */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    @Nullable
-    public Handler mContentCaptureHandler;
+    @NonNull
+    private final Handler mContentCaptureHandler;
 
     /**
      * Interface to the system_server binder object - it's only used to start the session (and
@@ -191,12 +187,6 @@
     @Nullable
     public ContentProtectionEventProcessor mContentProtectionEventProcessor;
 
-    /**
-     * A runnable object to perform the start of this session.
-     */
-    @Nullable
-    private Runnable mStartRunnable = null;
-
     private static class SessionStateReceiver extends IResultReceiver.Stub {
         private final WeakReference<MainContentCaptureSession> mMainSession;
 
@@ -208,7 +198,7 @@
         public void send(int resultCode, Bundle resultData) {
             final MainContentCaptureSession mainSession = mMainSession.get();
             if (mainSession == null) {
-                Log.w(TAG, "received result after main session released");
+                Log.w(TAG, "received result after mina session released");
                 return;
             }
             final IBinder binder;
@@ -223,8 +213,6 @@
                 binder = resultData.getBinder(EXTRA_BINDER);
                 if (binder == null) {
                     Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
-                    // explicitly init the bg thread
-                    mainSession.mContentCaptureHandler = mainSession.prepareContentCaptureHandler();
                     mainSession.runOnContentCaptureThread(() -> mainSession.resetSession(
                             STATE_DISABLED | STATE_INTERNAL_ERROR));
                     return;
@@ -232,45 +220,23 @@
             } else {
                 binder = null;
             }
-            // explicitly init the bg thread
-            mainSession.mContentCaptureHandler = mainSession.prepareContentCaptureHandler();
             mainSession.runOnContentCaptureThread(() ->
                     mainSession.onSessionStarted(resultCode, binder));
         }
     }
 
-    /**
-     * Prepares the content capture handler(i.e. the background thread).
-     *
-     * This is expected to be called from the {@link SessionStateReceiver#send} callback, after the
-     * session {@link performStart}. This is expected to be executed in a binder thread, instead
-     * of the UI thread.
-     */
-    @NonNull
-    private Handler prepareContentCaptureHandler() {
-        if (mContentCaptureHandler == null) {
-            try {
-                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-                    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareContentCaptureHandler");
-                }
-                mContentCaptureHandler = BackgroundThread.getHandler();
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-            }
-        }
-        return mContentCaptureHandler;
-    }
-
     /** @hide */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
     public MainContentCaptureSession(
             @NonNull ContentCaptureManager.StrippedContext context,
             @NonNull ContentCaptureManager manager,
             @NonNull Handler uiHandler,
+            @NonNull Handler contentCaptureHandler,
             @NonNull IContentCaptureManager systemServerInterface) {
         mContext = context;
         mManager = manager;
         mUiHandler = uiHandler;
+        mContentCaptureHandler = contentCaptureHandler;
         mSystemServerInterface = systemServerInterface;
 
         final int logHistorySize = mManager.mOptions.logHistorySize;
@@ -294,49 +260,18 @@
     }
 
     /**
-     * Performs the start of the session.
-     *
-     * This is expected to be called from the UI thread, when the activity finishes its first frame.
-     * This is a no-op if the session has already been started.
-     *
-     * See {@link #start(IBinder, IBinder, ComponentName, int)} for more details.
-     *
-     * @hide */
-    @Override
-    public void performStart() {
-        if (!hasStarted() && mStartRunnable != null) {
-            mStartRunnable.run();
-        }
-    }
-
-    /**
-     * Creates a runnable to start this session.
-     *
-     * For performance reasons, it is better to only create a task to start the session
-     * during the creation of the activity and perform the actual start when the activity
-     * finishes it's first frame.
+     * Starts this session.
      */
     @Override
     void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
             @NonNull ComponentName component, int flags) {
-        if (Flags.postCreateAndroidBgThread()) {
-            mStartRunnable = () -> {
-                try {
-                    if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-                        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "cc session startImpl");
-                    }
-                    startImpl(token, shareableActivityToken, component, flags);
-                } finally {
-                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-                }
-            };
-        } else {
-            startImpl(token, shareableActivityToken, component, flags);
-        }
+        runOnContentCaptureThread(
+                () -> startImpl(token, shareableActivityToken, component, flags));
     }
 
     private void startImpl(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
                @NonNull ComponentName component, int flags) {
+        checkOnContentCaptureThread();
         if (!isContentCaptureEnabled()) return;
 
         if (sVerbose) {
@@ -370,12 +305,11 @@
             Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
         }
     }
-
     @Override
     void onDestroy() {
         clearAndRunOnContentCaptureThread(() -> {
             try {
-                internalFlush(FLUSH_REASON_SESSION_FINISHED);
+                flush(FLUSH_REASON_SESSION_FINISHED);
             } finally {
                 destroySession();
             }
@@ -623,10 +557,11 @@
                 flushReason = forceFlush ? FLUSH_REASON_FORCE_FLUSH : FLUSH_REASON_FULL;
         }
 
-        internalFlush(flushReason);
+        flush(flushReason);
     }
 
     private boolean hasStarted() {
+        checkOnContentCaptureThread();
         return mState != UNKNOWN_STATE;
     }
 
@@ -640,11 +575,6 @@
             if (sVerbose) Log.v(TAG, "handleScheduleFlush(): session not started yet");
             return;
         }
-        if (mContentCaptureHandler == null) {
-            Log.w(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): content capture "
-                    + "thread not ready");
-            return;
-        }
 
         if (mDisabled.get()) {
             // Should not be called on this state, as handleSendEvent checks.
@@ -687,18 +617,15 @@
             if (sVerbose) Log.v(TAG, "Nothing to flush");
             return;
         }
-        internalFlush(reason);
+        flush(reason);
     }
 
-    /**
-     * Internal API to flush the buffered events to the service.
-     *
-     * Do not confuse this with the public API {@link #flush()}.
-     *
-     * @hide */
+    /** @hide */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     @Override
-    public void internalFlush(@FlushReason int reason) {
+    public void flush(@FlushReason int reason) {
+        // TODO: b/380381249 renaming the internal APIs to prevent confusions between this and the
+        // public API.
         runOnContentCaptureThread(() -> flushImpl(reason));
     }
 
@@ -720,11 +647,6 @@
         if (!isContentCaptureReceiverEnabled()) {
             return;
         }
-        if (mContentCaptureHandler == null) {
-            Log.w(TAG, "handleForceFlush(" + getDebugState(reason) + "): content capture thread"
-                    + "not ready");
-            return;
-        }
 
         if (mDirectServiceInterface == null) {
             if (sVerbose) {
@@ -841,9 +763,7 @@
         }
         mDirectServiceInterface = null;
         mContentProtectionEventProcessor = null;
-        if (mContentCaptureHandler != null) {
-            mContentCaptureHandler.removeMessages(MSG_FLUSH);
-        }
+        mContentCaptureHandler.removeMessages(MSG_FLUSH);
     }
 
     @Override
@@ -997,10 +917,6 @@
      * clear the buffer events then starting sending out current event.
      */
     private void enqueueEvent(@NonNull final ContentCaptureEvent event, boolean forceFlush) {
-        if (mContentCaptureHandler == null) {
-            mEventProcessQueue.offer(event);
-            return;
-        }
         if (forceFlush || mEventProcessQueue.size() >= mManager.mOptions.maxBufferSize - 1) {
             // The buffer events are cleared in the same thread first to prevent new events
             // being added during the time of context switch. This would disrupt the sequence
@@ -1203,10 +1119,6 @@
      * always delegate to the assigned thread from {@code mHandler} for synchronization.</p>
      */
     private void checkOnContentCaptureThread() {
-        if (mContentCaptureHandler == null) {
-            Log.e(TAG, "content capture thread is not initiallized!");
-            return;
-        }
         final boolean onContentCaptureThread = mContentCaptureHandler.getLooper().isCurrentThread();
         if (!onContentCaptureThread) {
             mWrongThreadCount.incrementAndGet();
@@ -1227,12 +1139,6 @@
      * </p>
      */
     private void runOnContentCaptureThread(@NonNull Runnable r) {
-        if (mContentCaptureHandler == null) {
-            Log.e(TAG, "content capture thread is not initiallized!");
-            // fall back to UI thread
-            runOnUiThread(r);
-            return;
-        }
         if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
             mContentCaptureHandler.post(r);
         } else {
@@ -1241,12 +1147,6 @@
     }
 
     private void clearAndRunOnContentCaptureThread(@NonNull Runnable r, int what) {
-        if (mContentCaptureHandler == null) {
-            Log.e(TAG, "content capture thread is not initiallized!");
-            // fall back to UI thread
-            runOnUiThread(r);
-            return;
-        }
         if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
             mContentCaptureHandler.removeMessages(what);
             mContentCaptureHandler.post(r);
diff --git a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
index 9df8350..e7bc004 100644
--- a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
+++ b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
@@ -15,14 +15,3 @@
     bug: "380381249"
     is_exported: true
 }
-
-flag {
-    name: "post_create_android_bg_thread"
-    namespace: "pixel_state_server"
-    description: "Feature flag to post create the bg thread when an app is in the allowlist"
-    bug: "376468525"
-    is_fixed_read_only: true
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e904345f..0fb8042 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -391,7 +391,8 @@
      */
     @Deprecated
     @GuardedBy("sLock")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+            publicAlternatives = "Use {@code Context#getSystemService(InputMethodManager.class)}.")
     static InputMethodManager sInstance;
 
     /**
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 4258ca4..16f4114 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -19,15 +19,6 @@
 }
 
 flag {
-    name: "imm_userhandle_hostsidetests"
-    is_exported: true
-    namespace: "input_method"
-    description: "Feature flag for replacing UserIdInt with UserHandle in some helper IMM functions"
-    bug: "301713309"
-    is_fixed_read_only: true
-}
-
-flag {
     name: "concurrent_input_methods"
     is_exported: true
     namespace: "input_method"
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7c75d7b..ec0d915 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -44,7 +44,6 @@
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.Application;
-import android.app.LoadedApk;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.appwidget.AppWidgetHostView;
@@ -160,6 +159,7 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -720,6 +720,11 @@
             // Nothing to visit by default.
         }
 
+        /** See {@link RemoteViews#visitIcons(Consumer)}. **/
+        public void visitIcons(@NonNull Consumer<Icon> visitor) {
+            // Nothing to visit by default.
+        }
+
         public abstract void writeToParcel(Parcel dest, int flags);
 
         /**
@@ -850,6 +855,29 @@
     }
 
     /**
+     * Note all {@link Icon} that are referenced internally.
+     * @hide
+     */
+    public void visitIcons(@NonNull Consumer<Icon> visitor) {
+        if (mActions != null) {
+            for (int i = 0; i < mActions.size(); i++) {
+                mActions.get(i).visitIcons(visitor);
+            }
+        }
+        if (mSizedRemoteViews != null) {
+            for (int i = 0; i < mSizedRemoteViews.size(); i++) {
+                mSizedRemoteViews.get(i).visitIcons(visitor);
+            }
+        }
+        if (mLandscape != null) {
+            mLandscape.visitIcons(visitor);
+        }
+        if (mPortrait != null) {
+            mPortrait.visitIcons(visitor);
+        }
+    }
+
+    /**
      * @hide
      * @return True if there is a change
      */
@@ -1313,6 +1341,19 @@
         }
 
         @Override
+        public void visitIcons(Consumer<Icon> visitor) {
+            if (mItems == null) {
+                RemoteCollectionItems cachedItems = mCollectionCache.getItemsForId(mIntentId);
+                if (cachedItems != null) {
+                    cachedItems.visitIcons(visitor);
+                }
+                return;
+            }
+
+            mItems.visitIcons(visitor);
+        }
+
+        @Override
         public boolean canWriteToProto() {
             // Skip actions that do not contain items (intent only actions)
             return mItems != null;
@@ -2386,7 +2427,7 @@
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         ArrayList<Bitmap> mBitmaps;
         SparseIntArray mBitmapHashes;
-        int mBitmapMemory = -1;
+        long mBitmapMemory = -1;
 
         public BitmapCache() {
             mBitmaps = new ArrayList<>();
@@ -2450,7 +2491,7 @@
             }
         }
 
-        public int getBitmapMemory() {
+        public long getBitmapMemory() {
             if (mBitmapMemory < 0) {
                 mBitmapMemory = 0;
                 int count = mBitmaps.size();
@@ -2736,6 +2777,13 @@
                 // TODO(b/281044385): Should we do anything about type BUNDLE?
             }
         }
+
+        @Override
+        public void visitIcons(@NonNull Consumer<Icon> visitor) {
+            if (mType == ICON && getParameterValue(null) instanceof Icon icon) {
+                visitor.accept(icon);
+            }
+        }
     }
 
     /** Class for the reflection actions. */
@@ -4140,6 +4188,11 @@
         }
 
         @Override
+        public void visitIcons(@NonNull Consumer<Icon> visitor) {
+            mNestedViews.visitIcons(visitor);
+        }
+
+        @Override
         public boolean canWriteToProto() {
             return true;
         }
@@ -6393,15 +6446,43 @@
     }
 
     /**
-     * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
+     * Returns an estimate of the bitmap heap memory usage by setBitmap and setImageViewBitmap in
+     * this RemoteViews.
+     *
+     * @hide
      */
-    /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public int estimateMemoryUsage() {
+    public long estimateMemoryUsage() {
         return mBitmapCache.getBitmapMemory();
     }
 
     /**
+     * Returns an estimate of bitmap heap memory usage by setIcon and setImageViewIcon in this
+     * RemoteViews. Note that this function will count duplicate Icons in its estimate.
+     *
+     * @hide
+     */
+    public long estimateIconMemoryUsage() {
+        AtomicLong total = new AtomicLong(0);
+        visitIcons(icon -> {
+            if (icon.getType() == Icon.TYPE_BITMAP || icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
+                total.addAndGet(icon.getBitmap().getAllocationByteCount());
+            }
+        });
+        return total.get();
+    }
+
+    /**
+     * Returns an estimate of the bitmap heap memory usage for all Icon and Bitmap actions in this
+     * RemoteViews.
+     *
+     * @hide
+     */
+    public long estimateTotalBitmapMemoryUsage() {
+        return estimateMemoryUsage() + estimateIconMemoryUsage();
+    }
+
+    /**
      * Add an action to be executed on the remote side when apply is called.
      *
      * @param a The action to add
@@ -8484,8 +8565,14 @@
                 return context;
             }
             try {
-                LoadedApk.checkAndUpdateApkPaths(mApplication);
-                Context applicationContext = context.createApplicationContext(mApplication,
+                // Use PackageManager as the source of truth for application information, rather
+                // than the parceled ApplicationInfo provided by the app.
+                ApplicationInfo sanitizedApplication =
+                        context.getPackageManager().getApplicationInfoAsUser(
+                                mApplication.packageName, 0,
+                                UserHandle.getUserId(mApplication.uid));
+                Context applicationContext = context.createApplicationContext(
+                        sanitizedApplication,
                         Context.CONTEXT_RESTRICTED);
                 // Get the correct apk paths while maintaining the current context's configuration.
                 return applicationContext.createConfigurationContext(
@@ -9763,6 +9850,15 @@
                 view.visitUris(visitor);
             }
         }
+
+        /**
+         * See {@link RemoteViews#visitIcons(Consumer)}.
+         */
+        private void visitIcons(@NonNull Consumer<Icon> visitor) {
+            for (RemoteViews view : mViews) {
+                view.visitIcons(visitor);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
new file mode 100644
index 0000000..7758dea
--- /dev/null
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -0,0 +1,149 @@
+/*
+ * 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 android.window;
+
+import android.annotation.Nullable;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.window.flags.Flags;
+
+import java.util.function.BooleanSupplier;
+
+/**
+ * Checks Desktop Experience flag state.
+ *
+ * <p>This enum provides a centralized way to control the behavior of flags related to desktop
+ * experience features which are aiming for developer preview before their release. It allows
+ * developer option to override the default behavior of these flags.
+ *
+ * <p>The flags here will be controlled by the {@code
+ * persist.wm.debug.desktop_experience_devopts} system property.
+ *
+ * <p>NOTE: Flags should only be added to this enum when they have received Product and UX alignment
+ * that the feature is ready for developer preview, otherwise just do a flag check.
+ *
+ * @hide
+ */
+public enum DesktopExperienceFlags {
+    ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(
+            com.android.server.display.feature.flags.Flags::enableDisplayContentModeManagement,
+            true),
+    ACTIVITY_EMBEDDING_SUPPORT_FOR_CONNECTED_DISPLAYS(
+            Flags::activityEmbeddingSupportForConnectedDisplays, false),
+    BASE_DENSITY_FOR_EXTERNAL_DISPLAYS(
+            com.android.server.display.feature.flags.Flags::baseDensityForExternalDisplays, true),
+    CONNECTED_DISPLAYS_CURSOR(com.android.input.flags.Flags::connectedDisplaysCursor, true),
+    DISPLAY_TOPOLOGY(com.android.server.display.feature.flags.Flags::displayTopology, true),
+    ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY(Flags::enableBugFixesForSecondaryDisplay, false),
+    ENABLE_CONNECTED_DISPLAYS_DND(Flags::enableConnectedDisplaysDnd, false),
+    ENABLE_CONNECTED_DISPLAYS_PIP(Flags::enableConnectedDisplaysPip, false),
+    ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG(Flags::enableConnectedDisplaysWindowDrag, false),
+    ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS(Flags::enableDisplayFocusInShellTransitions, false),
+    ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING(Flags::enableDisplayWindowingModeSwitching, false),
+    ENABLE_DRAG_TO_MAXIMIZE(Flags::enableDragToMaximize, true),
+    ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT(Flags::enableMoveToNextDisplayShortcut, false),
+    ENABLE_MULTIPLE_DESKTOPS_BACKEND(Flags::enableMultipleDesktopsBackend, false),
+    ENABLE_MULTIPLE_DESKTOPS_FRONTEND(Flags::enableMultipleDesktopsFrontend, false),
+    ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY(Flags::enablePerDisplayDesktopWallpaperActivity,
+            false),
+    ENABLE_PER_DISPLAY_PACKAGE_CONTEXT_CACHE_IN_STATUSBAR_NOTIF(
+            Flags::enablePerDisplayPackageContextCacheInStatusbarNotif, false),
+    ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAYS(Flags::enterDesktopByDefaultOnFreeformDisplays,
+            false),
+    REPARENT_WINDOW_TOKEN_API(Flags::reparentWindowTokenApi, true);
+
+    /**
+     * Flag class, to be used in case the enum cannot be used because the flag is not accessible.
+     *
+     * <p>This class will still use the process-wide cache.
+     */
+    public static class DesktopExperienceFlag {
+        // Function called to obtain aconfig flag value.
+        private final BooleanSupplier mFlagFunction;
+        // Whether the flag state should be affected by developer option.
+        private final boolean mShouldOverrideByDevOption;
+
+        public DesktopExperienceFlag(BooleanSupplier flagFunction,
+                boolean shouldOverrideByDevOption) {
+            this.mFlagFunction = flagFunction;
+            this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
+        }
+
+        /**
+         * Determines state of flag based on the actual flag and desktop experience developer option
+         * overrides.
+         */
+        public boolean isTrue() {
+            return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
+        }
+    }
+
+    private static final String TAG = "DesktopExperienceFlags";
+    // Function called to obtain aconfig flag value.
+    private final BooleanSupplier mFlagFunction;
+    // Whether the flag state should be affected by developer option.
+    private final boolean mShouldOverrideByDevOption;
+
+    // Local cache for toggle override, which is initialized once on its first access. It needs to
+    // be refreshed only on reboots as overridden state is expected to take effect on reboots.
+    @Nullable
+    private static Boolean sCachedToggleOverride;
+
+    public static final String SYSTEM_PROPERTY_NAME = "persist.wm.debug.desktop_experience_devopts";
+
+    DesktopExperienceFlags(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
+        this.mFlagFunction = flagFunction;
+        this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
+    }
+
+    /**
+     * Determines state of flag based on the actual flag and desktop experience developer option
+     * overrides.
+     */
+    public boolean isTrue() {
+        return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
+    }
+
+    private static boolean isFlagTrue(
+            BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
+        if (shouldOverrideByDevOption
+                && Flags.showDesktopExperienceDevOption()
+                && getToggleOverride()) {
+            return true;
+        }
+        return flagFunction.getAsBoolean();
+    }
+
+    private static boolean getToggleOverride() {
+        // If cached, return it
+        if (sCachedToggleOverride != null) {
+            return sCachedToggleOverride;
+        }
+
+        // Otherwise, fetch and cache it
+        boolean override = getToggleOverrideFromSystem();
+        sCachedToggleOverride = override;
+        Log.d(TAG, "Toggle override initialized to: " + override);
+        return override;
+    }
+
+    /** Returns the {@link ToggleOverride} from the system property.. */
+    private static boolean getToggleOverrideFromSystem() {
+        return SystemProperties.getBoolean(SYSTEM_PROPERTY_NAME, false);
+    }
+}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index d44b941..9468301 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -67,29 +67,37 @@
     ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
     ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS(
             Flags::enableDesktopWindowingTaskbarRunningApps, true),
-    // TODO: b/369763947 - remove this once ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS is ramped up
-    ENABLE_DESKTOP_WINDOWING_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
-    ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(
-            Flags::enableDesktopWindowingEnterTransitions, false),
-    ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false),
     ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
             Flags::enableWindowingTransitionHandlersObservers, false),
-    ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS(
-            Flags::enableDesktopAppLaunchAlttabTransitions, false),
-    ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS(
-            Flags::enableDesktopAppLaunchTransitions, false),
     ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false),
     ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true),
     ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX(
-            Flags::enableDesktopWindowingEnterTransitionBugfix, false),
+            Flags::enableDesktopWindowingEnterTransitionBugfix, true),
     ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX(
-            Flags::enableDesktopWindowingExitTransitionsBugfix, false),
+            Flags::enableDesktopWindowingExitTransitionsBugfix, true),
     ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX(
-            Flags::enableDesktopAppLaunchAlttabTransitionsBugfix, false),
+            Flags::enableDesktopAppLaunchAlttabTransitionsBugfix, true),
     ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX(
-            Flags::enableDesktopAppLaunchTransitionsBugfix, false),
+            Flags::enableDesktopAppLaunchTransitionsBugfix, true),
+    ENABLE_DESKTOP_COMPAT_UI_VISIBILITY_STATUS(
+            Flags::enableCompatUiVisibilityStatus, true),
+    ENABLE_DESKTOP_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE_BUGFIX(
+            Flags::skipCompatUiEducationInDesktopMode, true),
     INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
-            Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true);
+            Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true),
+    ENABLE_DESKTOP_WINDOWING_HSUM(Flags::enableDesktopWindowingHsum, true),
+    ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true),
+    ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
+    ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS(Flags::enableTaskResizingKeyboardShortcuts, true),
+    ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER(
+        Flags::enableDesktopWallpaperActivityForSystemUser, true),
+    ENABLE_TOP_VISIBLE_ROOT_TASK_PER_USER_TRACKING(
+        Flags::enableTopVisibleRootTaskPerUserTracking, true),
+    ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX(
+            Flags::enableDesktopRecentsTransitionsCornersBugfix, false),
+    ENABLE_DESKTOP_SYSTEM_DIALOGS_TRANSITIONS(Flags::enableDesktopSystemDialogsTransitions, true),
+    ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES(
+        Flags::enableDesktopWindowingMultiInstanceFeatures, true);
 
     /**
      * Flag class, to be used in case the enum cannot be used because the flag is not accessible.
@@ -142,28 +150,22 @@
         return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
     }
 
+    public static boolean isDesktopModeForcedEnabled() {
+        return getToggleOverride() == ToggleOverride.OVERRIDE_ON;
+    }
+
     private static boolean isFlagTrue(BooleanSupplier flagFunction,
             boolean shouldOverrideByDevOption) {
         if (!shouldOverrideByDevOption) return flagFunction.getAsBoolean();
         if (Flags.showDesktopExperienceDevOption()) {
-            return switch (getToggleOverride(null)) {
+            return switch (getToggleOverride()) {
                 case OVERRIDE_UNSET, OVERRIDE_OFF -> flagFunction.getAsBoolean();
                 case OVERRIDE_ON -> true;
             };
         }
         if (Flags.showDesktopWindowingDevOption()) {
-            Application application = ActivityThread.currentApplication();
-            if (application == null) {
-                Log.w(TAG, "Could not get the current application.");
-                return flagFunction.getAsBoolean();
-            }
-            ContentResolver contentResolver = application.getContentResolver();
-            if (contentResolver == null) {
-                Log.w(TAG, "Could not get the content resolver for the application.");
-                return flagFunction.getAsBoolean();
-            }
             boolean shouldToggleBeEnabledByDefault = Flags.enableDesktopWindowingMode();
-            return switch (getToggleOverride(contentResolver)) {
+            return switch (getToggleOverride()) {
                 case OVERRIDE_UNSET -> flagFunction.getAsBoolean();
                 // When toggle override matches its default state, don't override flags. This
                 // helps users reset their feature overrides.
@@ -174,14 +176,13 @@
         return flagFunction.getAsBoolean();
     }
 
-    private static ToggleOverride getToggleOverride(@Nullable ContentResolver contentResolver) {
+    private static ToggleOverride getToggleOverride() {
         // If cached, return it
         if (sCachedToggleOverride != null) {
             return sCachedToggleOverride;
         }
-
         // Otherwise, fetch and cache it
-        ToggleOverride override = getToggleOverrideFromSystem(contentResolver);
+        ToggleOverride override = getToggleOverrideFromSystem();
         sCachedToggleOverride = override;
         Log.d(TAG, "Toggle override initialized to: " + override);
         return override;
@@ -190,8 +191,7 @@
     /**
      *  Returns {@link ToggleOverride} from Settings.Global set by toggle.
      */
-    private static ToggleOverride getToggleOverrideFromSystem(
-            @Nullable ContentResolver contentResolver) {
+    private static ToggleOverride getToggleOverrideFromSystem() {
         int settingValue;
         if (Flags.showDesktopExperienceDevOption()) {
             settingValue = SystemProperties.getInt(
@@ -199,6 +199,16 @@
                     ToggleOverride.OVERRIDE_UNSET.getSetting()
             );
         } else {
+            final Application application = ActivityThread.currentApplication();
+            if (application == null) {
+                Log.w(TAG, "Could not get the current application.");
+                return ToggleOverride.OVERRIDE_UNSET;
+            }
+            final ContentResolver contentResolver = application.getContentResolver();
+            if (contentResolver == null) {
+                Log.w(TAG, "Could not get the content resolver for the application.");
+                return ToggleOverride.OVERRIDE_UNSET;
+            }
             settingValue = Settings.Global.getInt(
                     contentResolver,
                     Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
diff --git a/core/java/android/window/OWNERS b/core/java/android/window/OWNERS
index 77c99b9..82d3724 100644
--- a/core/java/android/window/OWNERS
+++ b/core/java/android/window/OWNERS
@@ -3,3 +3,4 @@
 include /services/core/java/com/android/server/wm/OWNERS
 
 per-file DesktopModeFlags.java = file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
+per-file DesktopExperienceFlags.java = file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index ce0ccd5..68b5a26 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -112,6 +112,12 @@
         mTaskFragmentOrganizer = null;
     }
 
+    /*
+     * ===========================================================================================
+     * Window container properties
+     * ===========================================================================================
+     */
+
     /**
      * Resize a container.
      */
@@ -170,20 +176,6 @@
     }
 
     /**
-     * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task
-     * has finished the enter animation with the given bounds.
-     */
-    @NonNull
-    public WindowContainerTransaction scheduleFinishEnterPip(
-            @NonNull WindowContainerToken container, @NonNull Rect bounds) {
-        final Change chg = getOrCreateChange(container.asBinder());
-        chg.mPinnedBounds = new Rect(bounds);
-        chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK;
-
-        return this;
-    }
-
-    /**
      * Send a SurfaceControl transaction to the server, which the server will apply in sync with
      * the next bounds change. As this uses deferred transaction and not BLAST it is only
      * able to sync with a single window, and the first visible window in this hierarchy of type
@@ -204,36 +196,6 @@
     }
 
     /**
-     * Like {@link #setBoundsChangeTransaction} but instead queues up a setPosition/WindowCrop
-     * on a container's surface control. This is useful when a boundsChangeTransaction needs to be
-     * queued up on a Task that won't be organized until the end of this window-container
-     * transaction.
-     *
-     * This requires that, at the end of this transaction, `task` will be organized; otherwise
-     * the server will throw an IllegalArgumentException.
-     *
-     * WARNING: Use this carefully. Whatever is set here should match the expected bounds after
-     *          the transaction completes since it will likely be replaced by it. This call is
-     *          intended to pre-emptively set bounds on a surface in sync with a buffer when
-     *          otherwise the new bounds and the new buffer would update on different frames.
-     *
-     * TODO(b/134365562): remove once TaskOrg drives full-screen or BLAST is enabled.
-     *
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction setBoundsChangeTransaction(
-            @NonNull WindowContainerToken task, @NonNull Rect surfaceBounds) {
-        Change chg = getOrCreateChange(task.asBinder());
-        if (chg.mBoundsChangeSurfaceBounds == null) {
-            chg.mBoundsChangeSurfaceBounds = new Rect();
-        }
-        chg.mBoundsChangeSurfaceBounds.set(surfaceBounds);
-        chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION_RECT;
-        return this;
-    }
-
-    /**
      * Set the windowing mode of children of a given root task, without changing
      * the windowing mode of the Task itself. This can be used during transitions
      * for example to make the activity render it's fullscreen configuration
@@ -381,22 +343,115 @@
     }
 
     /**
-     * Reparents a container into another one. The effect of a {@code null} parent can vary. For
-     * example, reparenting a stack to {@code null} will reparent it to its display.
+     * Sets whether a container is being drag-resized.
+     * When {@code true}, the client will reuse a single (larger) surface size to avoid
+     * continuous allocations on every size change.
      *
-     * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
-     *              the bottom.
+     * @param container WindowContainerToken of the task that changed its drag resizing state
+     * @hide
      */
     @NonNull
-    public WindowContainerTransaction reparent(@NonNull WindowContainerToken child,
-            @Nullable WindowContainerToken parent, boolean onTop) {
-        mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),
-                parent == null ? null : parent.asBinder(),
-                onTop));
+    public WindowContainerTransaction setDragResizing(@NonNull WindowContainerToken container,
+            boolean dragResizing) {
+        final Change change = getOrCreateChange(container.asBinder());
+        change.mChangeMask |= Change.CHANGE_DRAG_RESIZING;
+        change.mDragResizing = dragResizing;
         return this;
     }
 
     /**
+     * Sets/removes the always on top flag for this {@code windowContainer}. See
+     * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
+     * Please note that this method is only intended to be used for a
+     * {@link com.android.server.wm.Task} or {@link com.android.server.wm.DisplayArea}.
+     *
+     * <p>
+     *     Setting always on top to {@code True} will also make the {@code windowContainer} to move
+     *     to the top.
+     * </p>
+     * <p>
+     *     Setting always on top to {@code False} will make this {@code windowContainer} to move
+     *     below the other always on top sibling containers.
+     * </p>
+     *
+     * @param windowContainer the container which the flag need to be updated for.
+     * @param alwaysOnTop denotes whether or not always on top flag should be set.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction setAlwaysOnTop(
+            @NonNull WindowContainerToken windowContainer, boolean alwaysOnTop) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(
+                        HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP)
+                        .setContainer(windowContainer.asBinder())
+                        .setAlwaysOnTop(alwaysOnTop)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /**
+     * Sets/removes the reparent leaf task flag for this {@code windowContainer}.
+     * When this is set, the server side will try to reparent the leaf task to task display area
+     * if there is an existing activity in history during the activity launch. This operation only
+     * support on the organized root task.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction setReparentLeafTaskIfRelaunch(
+            @NonNull WindowContainerToken windowContainer, boolean reparentLeafTaskIfRelaunch) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(
+                        HierarchyOp.HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH)
+                        .setContainer(windowContainer.asBinder())
+                        .setReparentLeafTaskIfRelaunch(reparentLeafTaskIfRelaunch)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /**
+     * Defers client-facing configuration changes for activities in `container` until the end of
+     * the transition animation. The configuration will still be applied to the WMCore hierarchy
+     * at the normal time (beginning); so, special consideration must be made for this in the
+     * animation.
+     *
+     * @param container WindowContainerToken who's children should defer config notification.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction deferConfigToTransitionEnd(
+            @NonNull WindowContainerToken container) {
+        final Change change = getOrCreateChange(container.asBinder());
+        change.mConfigAtTransitionEnd = true;
+        return this;
+    }
+
+    /**
+     * Sets the task as trimmable or not. This can be used to prevent the task from being trimmed by
+     * recents. This attribute is set to true on task creation by default.
+     *
+     * @param isTrimmableFromRecents When {@code true}, task is set as trimmable from recents.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction setTaskTrimmableFromRecents(
+            @NonNull WindowContainerToken container,
+            boolean isTrimmableFromRecents) {
+        mHierarchyOps.add(
+                HierarchyOp.createForSetTaskTrimmableFromRecents(container.asBinder(),
+                        isTrimmableFromRecents));
+        return this;
+    }
+
+    /*
+     * ===========================================================================================
+     * Hierarchy updates (create/destroy/reorder/reparent containers)
+     * ===========================================================================================
+     */
+
+    /**
      * Reorders a container within its parent.
      *
      * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
@@ -425,6 +480,22 @@
     }
 
     /**
+     * Reparents a container into another one. The effect of a {@code null} parent can vary. For
+     * example, reparenting a stack to {@code null} will reparent it to its display.
+     *
+     * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
+     *              the bottom.
+     */
+    @NonNull
+    public WindowContainerTransaction reparent(@NonNull WindowContainerToken child,
+            @Nullable WindowContainerToken parent, boolean onTop) {
+        mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),
+                parent == null ? null : parent.asBinder(),
+                onTop));
+        return this;
+    }
+
+    /**
      * Reparent's all children tasks or the top task of {@param currentParent} in the specified
      * {@param windowingMode} and {@param activityType} to {@param newParent} in their current
      * z-order.
@@ -478,6 +549,116 @@
     }
 
     /**
+     * Finds and removes a task and its children using its container token. The task is removed
+     * from recents.
+     *
+     * If the task is a root task, its leaves are removed but the root task is not. Use
+     * {@link #removeRootTask(WindowContainerToken)} to remove the root task.
+     *
+     * @param containerToken ContainerToken of Task to be removed
+     */
+    @NonNull
+    public WindowContainerTransaction removeTask(@NonNull WindowContainerToken containerToken) {
+        mHierarchyOps.add(HierarchyOp.createForRemoveTask(containerToken.asBinder()));
+        return this;
+    }
+
+    /**
+     * Finds and removes a root task created by an organizer and its leaves using its container
+     * token.
+     *
+     * @param containerToken ContainerToken of the root task to be removed
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction removeRootTask(@NonNull WindowContainerToken containerToken) {
+        mHierarchyOps.add(HierarchyOp.createForRemoveRootTask(containerToken.asBinder()));
+        return this;
+    }
+
+    /**
+     * If `container` was brought to front as a transient-launch (eg. recents), this will reorder
+     * the container back to where it was prior to the transient-launch. This way if a transient
+     * launch is "aborted", the z-ordering of containers in WM should be restored to before the
+     * launch.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction restoreTransientOrder(
+            @NonNull WindowContainerToken container) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER)
+                        .setContainer(container.asBinder())
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /**
+     * Restore the back navigation target from visible to invisible for canceling gesture animation.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction restoreBackNavi() {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /*
+     * ===========================================================================================
+     * Activity launch
+     * ===========================================================================================
+     */
+
+    /**
+     * Starts a task by id. The task is expected to already exist (eg. as a recent task).
+     * @param taskId Id of task to start.
+     * @param options bundle containing ActivityOptions for the task's top activity.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
+        mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
+        return this;
+    }
+
+    /**
+     * Sends a pending intent in sync.
+     * @param sender The PendingIntent sender.
+     * @param intent The fillIn intent to patch over the sender's base intent.
+     * @param options bundle containing ActivityOptions for the task's top activity.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction sendPendingIntent(@Nullable PendingIntent sender,
+            @Nullable Intent intent, @Nullable Bundle options) {
+        mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
+                .setLaunchOptions(options)
+                .setPendingIntent(sender)
+                .setActivityIntent(intent)
+                .build());
+        return this;
+    }
+
+    /**
+     * Starts activity(s) from a shortcut.
+     * @param callingPackage The package launching the shortcut.
+     * @param shortcutInfo Information about the shortcut to start
+     * @param options bundle containing ActivityOptions for the task's top activity.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
+            @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
+        mHierarchyOps.add(HierarchyOp.createForStartShortcut(
+                callingPackage, shortcutInfo, options));
+        return this;
+    }
+
+    /**
      * Sets whether a container should be the launch root for the specified windowing mode and
      * activity type. This currently only applies to Task containers created by organizer.
      */
@@ -491,6 +672,12 @@
         return this;
     }
 
+    /*
+     * ===========================================================================================
+     * Multitasking
+     * ===========================================================================================
+     */
+
     /**
      * Sets two containers adjacent to each other. Containers below two visible adjacent roots will
      * be made invisible. This currently only applies to TaskFragment containers created by
@@ -599,93 +786,162 @@
         return this;
     }
 
+    /*
+     * ===========================================================================================
+     * PIP
+     * ===========================================================================================
+     */
+
     /**
-     * Starts a task by id. The task is expected to already exist (eg. as a recent task).
-     * @param taskId Id of task to start.
-     * @param options bundle containing ActivityOptions for the task's top activity.
+     * Moves the PiP activity of a parent task to a pinned root task.
+     * @param parentToken the parent task of the PiP activity
+     * @param bounds the entry bounds
      * @hide
      */
     @NonNull
-    public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
-        mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
-        return this;
-    }
-
-    /**
-     * Finds and removes a task and its children using its container token. The task is removed
-     * from recents.
-     *
-     * If the task is a root task, its leaves are removed but the root task is not. Use
-     * {@link #removeRootTask(WindowContainerToken)} to remove the root task.
-     *
-     * @param containerToken ContainerToken of Task to be removed
-     */
-    @NonNull
-    public WindowContainerTransaction removeTask(@NonNull WindowContainerToken containerToken) {
-        mHierarchyOps.add(HierarchyOp.createForRemoveTask(containerToken.asBinder()));
-        return this;
-    }
-
-    /**
-     * Finds and removes a root task created by an organizer and its leaves using its container
-     * token.
-     *
-     * @param containerToken ContainerToken of the root task to be removed
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction removeRootTask(@NonNull WindowContainerToken containerToken) {
-        mHierarchyOps.add(HierarchyOp.createForRemoveRootTask(containerToken.asBinder()));
-        return this;
-    }
-
-    /**
-     * Sets whether a container is being drag-resized.
-     * When {@code true}, the client will reuse a single (larger) surface size to avoid
-     * continuous allocations on every size change.
-     *
-     * @param container WindowContainerToken of the task that changed its drag resizing state
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction setDragResizing(@NonNull WindowContainerToken container,
-            boolean dragResizing) {
-        final Change change = getOrCreateChange(container.asBinder());
-        change.mChangeMask |= Change.CHANGE_DRAG_RESIZING;
-        change.mDragResizing = dragResizing;
-        return this;
-    }
-
-    /**
-     * Sends a pending intent in sync.
-     * @param sender The PendingIntent sender.
-     * @param intent The fillIn intent to patch over the sender's base intent.
-     * @param options bundle containing ActivityOptions for the task's top activity.
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction sendPendingIntent(@Nullable PendingIntent sender,
-            @Nullable Intent intent, @Nullable Bundle options) {
-        mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
-                .setLaunchOptions(options)
-                .setPendingIntent(sender)
-                .setActivityIntent(intent)
+    public WindowContainerTransaction movePipActivityToPinnedRootTask(
+            @NonNull WindowContainerToken parentToken, @NonNull Rect bounds) {
+        mHierarchyOps.add(new HierarchyOp
+                .Builder(HierarchyOp.HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK)
+                .setContainer(parentToken.asBinder())
+                .setBounds(bounds)
                 .build());
         return this;
     }
 
     /**
-     * Starts activity(s) from a shortcut.
-     * @param callingPackage The package launching the shortcut.
-     * @param shortcutInfo Information about the shortcut to start
-     * @param options bundle containing ActivityOptions for the task's top activity.
+     * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task
+     * has finished the enter animation with the given bounds.
+     */
+    @NonNull
+    public WindowContainerTransaction scheduleFinishEnterPip(
+            @NonNull WindowContainerToken container, @NonNull Rect bounds) {
+        final Change chg = getOrCreateChange(container.asBinder());
+        chg.mPinnedBounds = new Rect(bounds);
+        chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK;
+
+        return this;
+    }
+
+    /*
+     * ===========================================================================================
+     * Insets
+     * ===========================================================================================
+     */
+
+    /**
+     * Adds a given {@code Rect} as an insets source frame on the {@code receiver}.
+     *
+     * @param receiver The window container that the insets source is added to.
+     * @param owner    The owner of the insets source. An insets source can only be modified by its
+     *                 owner.
+     * @param index    An owner might add multiple insets sources with the same type.
+     *                 This identifies them.
+     * @param type     The {@link InsetsType} of the insets source.
+     * @param frame    The rectangle area of the insets source.
+     * @param boundingRects The bounding rects within this inset, relative to the |frame|.
      * @hide
      */
     @NonNull
-    public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
-            @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
-        mHierarchyOps.add(HierarchyOp.createForStartShortcut(
-                callingPackage, shortcutInfo, options));
+    public WindowContainerTransaction addInsetsSource(
+            @NonNull WindowContainerToken receiver,
+            @Nullable IBinder owner, int index, @InsetsType int type, @Nullable Rect frame,
+            @Nullable Rect[] boundingRects, @InsetsSource.Flags int flags) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER)
+                        .setContainer(receiver.asBinder())
+                        .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type)
+                                .setSource(InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE)
+                                .setArbitraryRectangle(frame)
+                                .setBoundingRects(boundingRects)
+                                .setFlags(flags))
+                        .setInsetsFrameOwner(owner)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /**
+     * Removes the insets source from the {@code receiver}.
+     *
+     * @param receiver The window container that the insets source was added to.
+     * @param owner    The owner of the insets source. An insets source can only be modified by its
+     *                 owner.
+     * @param index    An owner might add multiple insets sources with the same type.
+     *                 This identifies them.
+     * @param type     The {@link InsetsType} of the insets source.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction removeInsetsSource(@NonNull WindowContainerToken receiver,
+            @Nullable IBinder owner, int index, @InsetsType int type) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER)
+                        .setContainer(receiver.asBinder())
+                        .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type))
+                        .setInsetsFrameOwner(owner)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /*
+     * ===========================================================================================
+     * Keyguard
+     * ===========================================================================================
+     */
+
+    /**
+     * Adds a {@link KeyguardState} to apply to the given displays.
+     *
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction addKeyguardState(@NonNull KeyguardState keyguardState) {
+        Objects.requireNonNull(keyguardState);
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(
+                        HierarchyOp.HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE)
+                        .setKeyguardState(keyguardState)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /*
+     * ===========================================================================================
+     * Task fragments
+     * ===========================================================================================
+     */
+
+    /**
+     * Sets the {@link TaskFragmentOrganizer} that applies this {@link WindowContainerTransaction}.
+     * When this is set, the server side will not check for the permission of
+     * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, but will ensure this WCT only
+     * contains operations that are allowed for this organizer, such as modifying TaskFragments that
+     * are organized by this organizer.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction setTaskFragmentOrganizer(
+            @NonNull ITaskFragmentOrganizer organizer) {
+        mTaskFragmentOrganizer = organizer;
+        return this;
+    }
+
+    /**
+     * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
+     * trigger callback with this {@param errorCallbackToken}.
+     * @param errorCallbackToken    client provided token that will be passed back as parameter in
+     *                              the callback if there is an error on the server side.
+     * @see ITaskFragmentOrganizer#onTaskFragmentError
+     */
+    @NonNull
+    public WindowContainerTransaction setErrorCallbackToken(@NonNull IBinder errorCallbackToken) {
+        if (mErrorCallbackToken != null) {
+            throw new IllegalStateException("Can't set multiple error token for one transaction.");
+        }
+        mErrorCallbackToken = errorCallbackToken;
         return this;
     }
 
@@ -793,93 +1049,6 @@
     }
 
     /**
-     * If `container` was brought to front as a transient-launch (eg. recents), this will reorder
-     * the container back to where it was prior to the transient-launch. This way if a transient
-     * launch is "aborted", the z-ordering of containers in WM should be restored to before the
-     * launch.
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction restoreTransientOrder(
-            @NonNull WindowContainerToken container) {
-        final HierarchyOp hierarchyOp =
-                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER)
-                        .setContainer(container.asBinder())
-                        .build();
-        mHierarchyOps.add(hierarchyOp);
-        return this;
-    }
-
-    /**
-     * Restore the back navigation target from visible to invisible for canceling gesture animation.
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction restoreBackNavi() {
-        final HierarchyOp hierarchyOp =
-                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION)
-                        .build();
-        mHierarchyOps.add(hierarchyOp);
-        return this;
-    }
-
-    /**
-     * Adds a given {@code Rect} as an insets source frame on the {@code receiver}.
-     *
-     * @param receiver The window container that the insets source is added to.
-     * @param owner    The owner of the insets source. An insets source can only be modified by its
-     *                 owner.
-     * @param index    An owner might add multiple insets sources with the same type.
-     *                 This identifies them.
-     * @param type     The {@link InsetsType} of the insets source.
-     * @param frame    The rectangle area of the insets source.
-     * @param boundingRects The bounding rects within this inset, relative to the |frame|.
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction addInsetsSource(
-            @NonNull WindowContainerToken receiver,
-            @Nullable IBinder owner, int index, @InsetsType int type, @Nullable Rect frame,
-            @Nullable Rect[] boundingRects, @InsetsSource.Flags int flags) {
-        final HierarchyOp hierarchyOp =
-                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER)
-                        .setContainer(receiver.asBinder())
-                        .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type)
-                                .setSource(InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE)
-                                .setArbitraryRectangle(frame)
-                                .setBoundingRects(boundingRects)
-                                .setFlags(flags))
-                        .setInsetsFrameOwner(owner)
-                        .build();
-        mHierarchyOps.add(hierarchyOp);
-        return this;
-    }
-
-    /**
-     * Removes the insets source from the {@code receiver}.
-     *
-     * @param receiver The window container that the insets source was added to.
-     * @param owner    The owner of the insets source. An insets source can only be modified by its
-     *                 owner.
-     * @param index    An owner might add multiple insets sources with the same type.
-     *                 This identifies them.
-     * @param type     The {@link InsetsType} of the insets source.
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction removeInsetsSource(@NonNull WindowContainerToken receiver,
-            @Nullable IBinder owner, int index, @InsetsType int type) {
-        final HierarchyOp hierarchyOp =
-                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER)
-                        .setContainer(receiver.asBinder())
-                        .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type))
-                        .setInsetsFrameOwner(owner)
-                        .build();
-        mHierarchyOps.add(hierarchyOp);
-        return this;
-    }
-
-    /**
      * Requests focus on the top running Activity in the given TaskFragment. This will only take
      * effect if there is no focus, or if the current focus is in the same Task as the requested
      * TaskFragment.
@@ -961,157 +1130,6 @@
     }
 
     /**
-     * Adds a {@link KeyguardState} to apply to the given displays.
-     *
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction addKeyguardState(@NonNull KeyguardState keyguardState) {
-        Objects.requireNonNull(keyguardState);
-        final HierarchyOp hierarchyOp =
-                new HierarchyOp.Builder(
-                        HierarchyOp.HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE)
-                        .setKeyguardState(keyguardState)
-                        .build();
-        mHierarchyOps.add(hierarchyOp);
-        return this;
-    }
-
-    /**
-     * Sets/removes the always on top flag for this {@code windowContainer}. See
-     * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
-     * Please note that this method is only intended to be used for a
-     * {@link com.android.server.wm.Task} or {@link com.android.server.wm.DisplayArea}.
-     *
-     * <p>
-     *     Setting always on top to {@code True} will also make the {@code windowContainer} to move
-     *     to the top.
-     * </p>
-     * <p>
-     *     Setting always on top to {@code False} will make this {@code windowContainer} to move
-     *     below the other always on top sibling containers.
-     * </p>
-     *
-     * @param windowContainer the container which the flag need to be updated for.
-     * @param alwaysOnTop denotes whether or not always on top flag should be set.
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction setAlwaysOnTop(
-            @NonNull WindowContainerToken windowContainer, boolean alwaysOnTop) {
-        final HierarchyOp hierarchyOp =
-                new HierarchyOp.Builder(
-                        HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP)
-                        .setContainer(windowContainer.asBinder())
-                        .setAlwaysOnTop(alwaysOnTop)
-                        .build();
-        mHierarchyOps.add(hierarchyOp);
-        return this;
-    }
-
-    /**
-     * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
-     * trigger callback with this {@param errorCallbackToken}.
-     * @param errorCallbackToken    client provided token that will be passed back as parameter in
-     *                              the callback if there is an error on the server side.
-     * @see ITaskFragmentOrganizer#onTaskFragmentError
-     */
-    @NonNull
-    public WindowContainerTransaction setErrorCallbackToken(@NonNull IBinder errorCallbackToken) {
-        if (mErrorCallbackToken != null) {
-            throw new IllegalStateException("Can't set multiple error token for one transaction.");
-        }
-        mErrorCallbackToken = errorCallbackToken;
-        return this;
-    }
-
-    /**
-     * Sets the {@link TaskFragmentOrganizer} that applies this {@link WindowContainerTransaction}.
-     * When this is set, the server side will not check for the permission of
-     * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, but will ensure this WCT only
-     * contains operations that are allowed for this organizer, such as modifying TaskFragments that
-     * are organized by this organizer.
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction setTaskFragmentOrganizer(
-            @NonNull ITaskFragmentOrganizer organizer) {
-        mTaskFragmentOrganizer = organizer;
-        return this;
-    }
-
-    /**
-     * Sets/removes the reparent leaf task flag for this {@code windowContainer}.
-     * When this is set, the server side will try to reparent the leaf task to task display area
-     * if there is an existing activity in history during the activity launch. This operation only
-     * support on the organized root task.
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction setReparentLeafTaskIfRelaunch(
-            @NonNull WindowContainerToken windowContainer, boolean reparentLeafTaskIfRelaunch) {
-        final HierarchyOp hierarchyOp =
-                new HierarchyOp.Builder(
-                        HierarchyOp.HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH)
-                        .setContainer(windowContainer.asBinder())
-                        .setReparentLeafTaskIfRelaunch(reparentLeafTaskIfRelaunch)
-                        .build();
-        mHierarchyOps.add(hierarchyOp);
-        return this;
-    }
-
-    /**
-     * Moves the PiP activity of a parent task to a pinned root task.
-     * @param parentToken the parent task of the PiP activity
-     * @param bounds the entry bounds
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction movePipActivityToPinnedRootTask(
-            @NonNull WindowContainerToken parentToken, @NonNull Rect bounds) {
-        mHierarchyOps.add(new HierarchyOp
-                .Builder(HierarchyOp.HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK)
-                .setContainer(parentToken.asBinder())
-                .setBounds(bounds)
-                .build());
-        return this;
-    }
-
-    /**
-     * Defers client-facing configuration changes for activities in `container` until the end of
-     * the transition animation. The configuration will still be applied to the WMCore hierarchy
-     * at the normal time (beginning); so, special consideration must be made for this in the
-     * animation.
-     *
-     * @param container WindowContainerToken who's children should defer config notification.
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction deferConfigToTransitionEnd(
-            @NonNull WindowContainerToken container) {
-        final Change change = getOrCreateChange(container.asBinder());
-        change.mConfigAtTransitionEnd = true;
-        return this;
-    }
-
-    /**
-     * Sets the task as trimmable or not. This can be used to prevent the task from being trimmed by
-     * recents. This attribute is set to true on task creation by default.
-     *
-     * @param isTrimmableFromRecents When {@code true}, task is set as trimmable from recents.
-     * @hide
-     */
-    @NonNull
-    public WindowContainerTransaction setTaskTrimmableFromRecents(
-            @NonNull WindowContainerToken container,
-            boolean isTrimmableFromRecents) {
-        mHierarchyOps.add(
-                HierarchyOp.createForSetTaskTrimmableFromRecents(container.asBinder(),
-                        isTrimmableFromRecents));
-        return this;
-    }
-
-    /**
      * Merges another WCT into this one.
      * @param transfer When true, this will transfer everything from other potentially leaving
      *                 other in an unusable state. When false, other is left alone, but
@@ -1206,7 +1224,7 @@
 
     @NonNull
     public static final Creator<WindowContainerTransaction> CREATOR =
-            new Creator<WindowContainerTransaction>() {
+            new Creator<>() {
                 @Override
                 public WindowContainerTransaction createFromParcel(@NonNull Parcel in) {
                     return new WindowContainerTransaction(in);
@@ -1227,19 +1245,17 @@
         public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;
         public static final int CHANGE_PIP_CALLBACK = 1 << 2;
         public static final int CHANGE_HIDDEN = 1 << 3;
-        public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4;
-        public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
-        public static final int CHANGE_FORCE_NO_PIP = 1 << 6;
-        public static final int CHANGE_FORCE_TRANSLUCENT = 1 << 7;
-        public static final int CHANGE_DRAG_RESIZING = 1 << 8;
-        public static final int CHANGE_RELATIVE_BOUNDS = 1 << 9;
+        public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 4;
+        public static final int CHANGE_FORCE_NO_PIP = 1 << 5;
+        public static final int CHANGE_FORCE_TRANSLUCENT = 1 << 6;
+        public static final int CHANGE_DRAG_RESIZING = 1 << 7;
+        public static final int CHANGE_RELATIVE_BOUNDS = 1 << 8;
 
         @IntDef(flag = true, prefix = { "CHANGE_" }, value = {
                 CHANGE_FOCUSABLE,
                 CHANGE_BOUNDS_TRANSACTION,
                 CHANGE_PIP_CALLBACK,
                 CHANGE_HIDDEN,
-                CHANGE_BOUNDS_TRANSACTION_RECT,
                 CHANGE_IGNORE_ORIENTATION_REQUEST,
                 CHANGE_FORCE_NO_PIP,
                 CHANGE_FORCE_TRANSLUCENT,
@@ -1262,7 +1278,6 @@
 
         private Rect mPinnedBounds = null;
         private SurfaceControl.Transaction mBoundsChangeTransaction = null;
-        private Rect mBoundsChangeSurfaceBounds = null;
         @Nullable
         private Rect mRelativeBounds = null;
         private boolean mConfigAtTransitionEnd = false;
@@ -1290,10 +1305,6 @@
                 mBoundsChangeTransaction =
                     SurfaceControl.Transaction.CREATOR.createFromParcel(in);
             }
-            if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION_RECT) != 0) {
-                mBoundsChangeSurfaceBounds = new Rect();
-                mBoundsChangeSurfaceBounds.readFromParcel(in);
-            }
             if ((mChangeMask & Change.CHANGE_RELATIVE_BOUNDS) != 0) {
                 mRelativeBounds = new Rect();
                 mRelativeBounds.readFromParcel(in);
@@ -1342,10 +1353,6 @@
             if (other.mWindowingMode >= WINDOWING_MODE_UNDEFINED) {
                 mWindowingMode = other.mWindowingMode;
             }
-            if (other.mBoundsChangeSurfaceBounds != null) {
-                mBoundsChangeSurfaceBounds = transfer ? other.mBoundsChangeSurfaceBounds
-                        : new Rect(other.mBoundsChangeSurfaceBounds);
-            }
             if (other.mRelativeBounds != null) {
                 mRelativeBounds = transfer
                         ? other.mRelativeBounds
@@ -1446,11 +1453,6 @@
         }
 
         @Nullable
-        public Rect getBoundsChangeSurfaceBounds() {
-            return mBoundsChangeSurfaceBounds;
-        }
-
-        @Nullable
         public Rect getRelativeBounds() {
             return mRelativeBounds;
         }
@@ -1529,9 +1531,6 @@
             if (mBoundsChangeTransaction != null) {
                 mBoundsChangeTransaction.writeToParcel(dest, flags);
             }
-            if (mBoundsChangeSurfaceBounds != null) {
-                mBoundsChangeSurfaceBounds.writeToParcel(dest, flags);
-            }
             if (mRelativeBounds != null) {
                 mRelativeBounds.writeToParcel(dest, flags);
             }
diff --git a/core/java/android/window/flags/device_state_auto_rotate_setting.aconfig b/core/java/android/window/flags/device_state_auto_rotate_setting.aconfig
new file mode 100644
index 0000000..bb66989
--- /dev/null
+++ b/core/java/android/window/flags/device_state_auto_rotate_setting.aconfig
@@ -0,0 +1,22 @@
+package: "com.android.window.flags"
+container: "system"
+
+flag {
+    name: "enable_device_state_auto_rotate_setting_logging"
+    namespace: "windowing_frontend"
+    description: "Enable device state auto rotate setting logging"
+    bug: "391147112"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "enable_device_state_auto_rotate_setting_refactor"
+    namespace: "windowing_frontend"
+    description: "Enable refactored device state auto rotate setting logic"
+    bug: "350946537"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 73ebcdd..d20b067 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -563,3 +563,50 @@
     description: "Replace the freeform windowing dev options with a desktop experience one."
     bug: "389092752"
 }
+
+flag {
+    name: "enable_quickswitch_desktop_split_bugfix"
+    namespace: "lse_desktop_experience"
+    description: "Enables splitting QuickSwitch between fullscreen apps and Desktop workspaces."
+    bug: "345296916"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "enable_desktop_windowing_exit_by_minimize_transition_bugfix"
+    namespace: "lse_desktop_experience"
+    description: "Enables exit desktop windowing by minimize transition & motion polish changes"
+    bug: "390161102"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "enable_start_launch_transition_from_taskbar_bugfix"
+    namespace: "lse_desktop_experience"
+    description: "Enables starting a launch transition directly from the taskbar if desktop tasks are visible."
+    bug: "361366053"
+    metadata {
+       purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "enable_non_default_display_split"
+    namespace: "lse_desktop_experience"
+    description: "Enables split screen on non default displays"
+    bug: "384999213"
+}
+
+flag {
+    name: "enable_desktop_mode_through_dev_option"
+    namespace: "lse_desktop_experience"
+    description: "Enables support for desktop mode through developer options for devices eligible for desktop mode."
+    bug: "382238347"
+    metadata {
+       purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 4b5adfc..3621981 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -81,3 +81,10 @@
     description: "Strict mode violation triggered by grace period usage"
     bug: "384807495"
 }
+
+flag {
+    name: "bal_clear_allowlist_duration"
+    namespace: "responsible_apis"
+    description: "Clear the allowlist duration when clearAllowBgActivityStarts is called"
+    bug: "322159724"
+}
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 9d7bedc..ea54395 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -599,24 +599,35 @@
                 Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
         sanitizeIntent(forwardIntent);
 
-        Intent intentToCheck = forwardIntent;
-        if (Intent.ACTION_CHOOSER.equals(forwardIntent.getAction())) {
+        if (!canForwardInner(forwardIntent, sourceUserId, targetUserId, packageManager,
+                contentResolver)) {
             return null;
         }
         if (forwardIntent.getSelector() != null) {
-            intentToCheck = forwardIntent.getSelector();
+            sanitizeIntent(forwardIntent.getSelector());
+            if (!canForwardInner(forwardIntent.getSelector(), sourceUserId, targetUserId,
+                    packageManager, contentResolver)) {
+                return null;
+            }
         }
-        String resolvedType = intentToCheck.resolveTypeIfNeeded(contentResolver);
-        sanitizeIntent(intentToCheck);
+        return forwardIntent;
+    }
+
+    private static boolean canForwardInner(Intent intent, int sourceUserId, int targetUserId,
+            IPackageManager packageManager, ContentResolver contentResolver) {
+        if (Intent.ACTION_CHOOSER.equals(intent.getAction())) {
+            return false;
+        }
+        String resolvedType = intent.resolveTypeIfNeeded(contentResolver);
         try {
             if (packageManager.canForwardTo(
-                    intentToCheck, resolvedType, sourceUserId, targetUserId)) {
-                return forwardIntent;
+                    intent, resolvedType, sourceUserId, targetUserId)) {
+                return true;
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "PackageManagerService is dead?");
         }
-        return null;
+        return false;
     }
 
     /**
diff --git a/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java b/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java
index dbbe4b9..0b04477 100644
--- a/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java
+++ b/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java
@@ -23,8 +23,10 @@
  * Provider of timing pulse that uses SurfaceFlinger Vsync Choreographer for frame callbacks.
  *
  * @hide
+ * @deprecated See b/222698397 - use vsync IDs instead.
  */
 // TODO(b/222698397): remove getSfInstance/this class usage and use vsyncId for transactions
+@Deprecated
 public final class SfVsyncFrameCallbackProvider implements AnimationFrameCallbackProvider {
 
     private final Choreographer mChoreographer;
@@ -61,4 +63,4 @@
     public void setFrameDelay(long delay) {
         Choreographer.setFrameDelay(delay);
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index c85b5d7..af763e4 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -58,15 +58,23 @@
         mRawArgs = args;
         mArgs.init(null, null, null, null, args, 0);
 
+        int status = 1;
         try {
             onRun();
+            status = 0;
         } catch (IllegalArgumentException e) {
             onShowUsage(System.err);
             System.err.println();
             System.err.println("Error: " + e.getMessage());
+            status = 0;
         } catch (Exception e) {
             e.printStackTrace(System.err);
-            System.exit(1);
+        } finally {
+            System.out.flush();
+            System.err.flush();
+        }
+        if (status != 0) {
+            System.exit(status);
         }
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index dc440e3..f49c5f1 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -84,7 +84,7 @@
     private static final String TAG = "BatteryStatsHistory";
 
     // Current on-disk Parcel version. Must be updated when the format of the parcelable changes
-    private static final int VERSION = 211;
+    private static final int VERSION = 212;
 
     private static final String HISTORY_DIR = "battery-history";
     private static final String FILE_SUFFIX = ".bh";
@@ -211,6 +211,8 @@
     private final MonotonicClock mMonotonicClock;
     // Monotonic time when we started writing to the history buffer
     private long mHistoryBufferStartTime;
+    // Monotonic time when the last event was written to the history buffer
+    private long mHistoryMonotonicEndTime;
     // Monotonically increasing size of written history
     private long mMonotonicHistorySize;
     private final ArraySet<PowerStats.Descriptor> mWrittenPowerStatsDescriptors = new ArraySet<>();
@@ -423,13 +425,22 @@
             return file;
         }
 
-        void writeToParcel(Parcel out, boolean useBlobs) {
+        void writeToParcel(Parcel out, boolean useBlobs,
+                long preferredEarliestIncludedTimestampMs) {
             Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.writeToParcel");
             lock();
             try {
                 final long start = SystemClock.uptimeMillis();
-                out.writeInt(mHistoryFiles.size() - 1);
                 for (int i = 0; i < mHistoryFiles.size() - 1; i++) {
+                    long monotonicEndTime = Long.MAX_VALUE;
+                    if (i < mHistoryFiles.size() - 1) {
+                        monotonicEndTime = mHistoryFiles.get(i + 1).monotonicTimeMs;
+                    }
+
+                    if (monotonicEndTime < preferredEarliestIncludedTimestampMs) {
+                        continue;
+                    }
+
                     AtomicFile file = mHistoryFiles.get(i).atomicFile;
                     byte[] raw = new byte[0];
                     try {
@@ -437,6 +448,8 @@
                     } catch (Exception e) {
                         Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e);
                     }
+
+                    out.writeBoolean(true);
                     if (useBlobs) {
                         out.writeBlob(raw);
                     } else {
@@ -444,6 +457,7 @@
                         out.writeByteArray(raw);
                     }
                 }
+                out.writeBoolean(false);
                 if (DEBUG) {
                     Slog.d(TAG,
                             "writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start));
@@ -634,6 +648,7 @@
         mWritableHistory = writableHistory;
         if (mWritableHistory != null) {
             mMutable = false;
+            mHistoryMonotonicEndTime = mWritableHistory.mHistoryMonotonicEndTime;
         }
 
         if (historyBuffer != null) {
@@ -937,6 +952,8 @@
                 }
                 // skip monotonic time field.
                 p.readLong();
+                // skip monotonic end time field
+                p.readLong();
                 // skip monotonic size field
                 p.readLong();
 
@@ -996,6 +1013,8 @@
             }
             // skip monotonic time field.
             out.readLong();
+            // skip monotonic end time field
+            out.readLong();
             // skip monotonic size field
             out.readLong();
             return true;
@@ -1024,6 +1043,7 @@
         p.setDataPosition(0);
         p.readInt();        // Skip the version field
         long monotonicTime = p.readLong();
+        p.readLong();       // Skip monotonic end time field
         p.readLong();       // Skip monotonic size field
         p.setDataPosition(pos);
         return monotonicTime;
@@ -1086,7 +1106,10 @@
     public void writeToParcel(Parcel out) {
         synchronized (this) {
             writeHistoryBuffer(out);
-            writeToParcel(out, false /* useBlobs */);
+            /* useBlobs */
+            if (mHistoryDir != null) {
+                mHistoryDir.writeToParcel(out, false /* useBlobs */, 0);
+            }
         }
     }
 
@@ -1096,16 +1119,13 @@
      *
      * @param out the output parcel
      */
-    public void writeToBatteryUsageStatsParcel(Parcel out) {
+    public void writeToBatteryUsageStatsParcel(Parcel out, long preferredHistoryDurationMs) {
         synchronized (this) {
             out.writeBlob(mHistoryBuffer.marshall());
-            writeToParcel(out, true /* useBlobs */);
-        }
-    }
-
-    private void writeToParcel(Parcel out, boolean useBlobs) {
-        if (mHistoryDir != null) {
-            mHistoryDir.writeToParcel(out, useBlobs);
+            if (mHistoryDir != null) {
+                mHistoryDir.writeToParcel(out, true /* useBlobs */,
+                        mHistoryMonotonicEndTime - preferredHistoryDurationMs);
+            }
         }
     }
 
@@ -1166,8 +1186,7 @@
     private void readFromParcel(Parcel in, boolean useBlobs) {
         final long start = SystemClock.uptimeMillis();
         mHistoryParcels = new ArrayList<>();
-        final int count = in.readInt();
-        for (int i = 0; i < count; i++) {
+        while (in.readBoolean()) {
             byte[] temp = useBlobs ? in.readBlob() : in.createByteArray();
             if (temp == null || temp.length == 0) {
                 continue;
@@ -2081,6 +2100,8 @@
      */
     @GuardedBy("this")
     private void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
+        mHistoryMonotonicEndTime = cur.time;
+
         if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
             dest.writeInt(BatteryStatsHistory.DELTA_TIME_ABS);
             cur.writeToParcel(dest, 0);
@@ -2396,6 +2417,7 @@
             }
 
             mHistoryBufferStartTime = in.readLong();
+            mHistoryMonotonicEndTime = in.readLong();
             mMonotonicHistorySize = in.readLong();
 
             mHistoryBuffer.setDataSize(0);
@@ -2424,6 +2446,7 @@
     private void writeHistoryBuffer(Parcel out) {
         out.writeInt(BatteryStatsHistory.VERSION);
         out.writeLong(mHistoryBufferStartTime);
+        out.writeLong(mHistoryMonotonicEndTime);
         out.writeLong(mMonotonicHistorySize);
         out.writeInt(mHistoryBuffer.dataSize());
         if (DEBUG) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ec39752..98d1ef6 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -51,6 +51,14 @@
 
     void showWirelessChargingAnimation(int batteryLevel);
 
+    /**
+     * Sets the new IME window status.
+     *
+     * @param displayId The id of the display to which the IME is bound.
+     * @param vis The IME window visibility.
+     * @param backDisposition The IME back disposition mode.
+     * @param showImeSwitcher Whether the IME Switcher button should be shown.
+     */
     void setImeWindowStatus(int displayId, int vis, int backDisposition, boolean showImeSwitcher);
     void setWindowState(int display, int window, int state);
 
@@ -209,9 +217,9 @@
     void setUdfpsRefreshRateCallback(in IUdfpsRefreshRateRequestCallback callback);
 
     /**
-     * Notifies System UI that the display is ready to show system decorations.
+     * Notifies System UI that the system decorations should be added on the display.
      */
-    void onDisplayReady(int displayId);
+    void onDisplayAddSystemDecorations(int displayId);
 
     /**
      * Notifies System UI that the system decorations should be removed from the display.
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index ec0954d..63d031c 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -61,6 +61,14 @@
     void setIconVisibility(String slot, boolean visible);
     @UnsupportedAppUsage
     void removeIcon(String slot);
+    /**
+     * Sets the new IME window status.
+     *
+     * @param displayId The id of the display to which the IME is bound.
+     * @param vis The IME window visibility.
+     * @param backDisposition The IME back disposition mode.
+     * @param showImeSwitcher Whether the IME Switcher button should be shown.
+     */
     void setImeWindowStatus(int displayId, int vis, int backDisposition, boolean showImeSwitcher);
     void expandSettingsPanel(String subPanel);
 
@@ -236,10 +244,4 @@
 
     /** Shows rear display educational dialog */
     void showRearDisplayDialog(int currentBaseState);
-
-    /** Unbundle a categorized notification */
-    void unbundleNotification(String key);
-
-    /** Rebundle an (un)categorized notification */
-    void rebundleNotification(String key);
 }
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index b3ab5d3..04ce9bc 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -105,6 +105,7 @@
     private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
     private Person mUser;
     private CharSequence mNameReplacement;
+    private CharSequence mSummarizedContent;
     private boolean mIsCollapsed;
     private ImageResolver mImageResolver;
     private CachingIconView mConversationIconView;
@@ -397,7 +398,7 @@
      *
      * @param isCollapsed is it collapsed
      */
-    @RemotableViewMethod
+    @RemotableViewMethod(asyncImpl = "setIsCollapsedAsync")
     public void setIsCollapsed(boolean isCollapsed) {
         mIsCollapsed = isCollapsed;
         mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE);
@@ -406,6 +407,15 @@
     }
 
     /**
+     * setDataAsync needs to do different stuff for the collapsed vs expanded view, so store the
+     * collapsed state early.
+     */
+    public Runnable setIsCollapsedAsync(boolean isCollapsed) {
+        mIsCollapsed = isCollapsed;
+        return () -> setIsCollapsed(isCollapsed);
+    }
+
+    /**
      * Set conversation data
      *
      * @param extras Bundle contains conversation data
@@ -439,8 +449,16 @@
                 extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
         int unreadCount = extras.getInt(Notification.EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT);
 
-        final List<MessagingMessage> newMessagingMessages =
-                createMessages(newMessages, /* isHistoric= */false, usePrecomputedText);
+        List<MessagingMessage> newMessagingMessages;
+        mSummarizedContent = extras.getCharSequence(Notification.EXTRA_SUMMARIZED_CONTENT);
+        if (mSummarizedContent != null && mIsCollapsed) {
+            Notification.MessagingStyle.Message summary =
+                    new Notification.MessagingStyle.Message(mSummarizedContent,  0, "");
+            newMessagingMessages = createMessages(List.of(summary), false, usePrecomputedText);
+        } else {
+            newMessagingMessages =
+                    createMessages(newMessages, /* isHistoric= */false, usePrecomputedText);
+        }
         final List<MessagingMessage> newHistoricMessagingMessages =
                 createMessages(newHistoricMessages, /* isHistoric= */true, usePrecomputedText);
 
@@ -463,7 +481,7 @@
 
         return new MessagingData(user, showSpinner, unreadCount,
                 newHistoricMessagingMessages, newMessagingMessages, groups, senders,
-                conversationHeaderData);
+                conversationHeaderData, mSummarizedContent);
     }
 
     /**
@@ -1622,6 +1640,9 @@
 
     @Nullable
     public CharSequence getConversationText() {
+        if (mSummarizedContent != null) {
+            return mSummarizedContent;
+        }
         if (mMessages.isEmpty()) {
             return null;
         }
diff --git a/core/java/com/android/internal/widget/MessagingData.java b/core/java/com/android/internal/widget/MessagingData.java
index fb1f28f..cb5041e 100644
--- a/core/java/com/android/internal/widget/MessagingData.java
+++ b/core/java/com/android/internal/widget/MessagingData.java
@@ -32,6 +32,7 @@
     private final List<List<MessagingMessage>> mGroups;
     private final List<Person> mSenders;
     private final int mUnreadCount;
+    private final CharSequence mSummarization;
 
     private ConversationHeaderData mConversationHeaderData;
 
@@ -41,8 +42,7 @@
             List<Person> senders) {
         this(user, showSpinner, /* unreadCount= */0,
                 historicMessagingMessages, newMessagingMessages,
-                groups,
-                senders, null);
+                groups, senders, null, null);
     }
 
     MessagingData(Person user, boolean showSpinner,
@@ -51,7 +51,8 @@
             List<MessagingMessage> newMessagingMessages,
             List<List<MessagingMessage>> groups,
             List<Person> senders,
-            @Nullable ConversationHeaderData conversationHeaderData) {
+            @Nullable ConversationHeaderData conversationHeaderData,
+            CharSequence summarization) {
         mUser = user;
         mShowSpinner = showSpinner;
         mUnreadCount = unreadCount;
@@ -60,6 +61,7 @@
         mGroups = groups;
         mSenders = senders;
         mConversationHeaderData = conversationHeaderData;
+        mSummarization = summarization;
     }
 
     public Person getUser() {
@@ -94,4 +96,9 @@
     public ConversationHeaderData getConversationHeaderData() {
         return mConversationHeaderData;
     }
+
+    @Nullable
+    public CharSequence getSummarization() {
+        return mSummarization;
+    }
 }
diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java
index a59ee77..c7f2283 100644
--- a/core/java/com/android/internal/widget/MessagingMessage.java
+++ b/core/java/com/android/internal/widget/MessagingMessage.java
@@ -24,7 +24,7 @@
 import java.util.Objects;
 
 /**
- * A message of a {@link MessagingLayout}.
+ * A message or summary of a {@link MessagingLayout}.
  */
 public interface MessagingMessage extends MessagingLinearLayout.MessagingChild {
 
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 973fd7e..5e82772 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -830,12 +830,12 @@
     }
 
     /**
-     * Get a color with an opacity that's 40% of the input color.
+     * Get a color with an opacity that's 50% of the input color.
      */
     @ColorInt
     static int getFadedColor(@ColorInt int color) {
         return Color.argb(
-                (int) (Color.alpha(color) * 0.4f + 0.5f),
+                (int) (Color.alpha(color) * 0.5f + 0.5f),
                 Color.red(color),
                 Color.green(color),
                 Color.blue(color));
@@ -1193,7 +1193,7 @@
          * <p>
          * <pre>
          *     When mFaded is set to true, a combination of the following is done to the segment:
-         *       1. The drawing color is mColor with opacity updated to 40%.
+         *       1. The drawing color is mColor with opacity updated to 50%.
          *       2. The gap between faded and non-faded segments is:
          *          - the segment-segment gap, when there is no tracker icon
          *          - 0, when there is tracker icon
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index 4ece81c..30dcc67 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -132,6 +132,8 @@
         final float centerY = (float) getBounds().centerY();
 
         final int numParts = mParts.size();
+        final float pointTop = Math.round(centerY - pointRadius);
+        final float pointBottom = Math.round(centerY + pointRadius);
         for (int iPart = 0; iPart < numParts; iPart++) {
             final DrawablePart part = mParts.get(iPart);
             final float start = left + part.mStart;
@@ -146,12 +148,13 @@
 
                 mFillPaint.setColor(segment.mColor);
 
-                mSegRectF.set(start, centerY - radiusY, end, centerY + radiusY);
+                mSegRectF.set(Math.round(start), Math.round(centerY - radiusY), Math.round(end),
+                        Math.round(centerY + radiusY));
                 canvas.drawRoundRect(mSegRectF, cornerRadius, cornerRadius, mFillPaint);
             } else if (part instanceof DrawablePoint point) {
                 // TODO: b/367804171 - actually use a vector asset for the default point
                 //  rather than drawing it as a box?
-                mPointRectF.set(start, centerY - pointRadius, end, centerY + pointRadius);
+                mPointRectF.set(Math.round(start), pointTop, Math.round(end), pointBottom);
                 final float inset = mState.mPointRectInset;
                 final float cornerRadius = mState.mPointRectCornerRadius;
                 mPointRectF.inset(inset, inset);
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
index 2cd4f03..52d5153 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
@@ -21,6 +21,7 @@
 
 import com.android.internal.widget.remotecompose.core.CoreDocument;
 import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
@@ -45,9 +46,11 @@
  */
 public class CoreDocumentAccessibility implements RemoteComposeDocumentAccessibility {
     private final CoreDocument mDocument;
+    private final RemoteContext mRemoteContext;
 
-    public CoreDocumentAccessibility(CoreDocument document) {
+    public CoreDocumentAccessibility(CoreDocument document, RemoteContext remoteContext) {
         this.mDocument = document;
+        this.mRemoteContext = remoteContext;
     }
 
     @Nullable
@@ -95,7 +98,7 @@
     @Override
     public boolean performAction(Component component, int action, Bundle arguments) {
         if (action == ACTION_CLICK) {
-            mDocument.performClick(component.getComponentId());
+            mDocument.performClick(mRemoteContext, component.getComponentId());
             return true;
         } else {
             return false;
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeAccessibilityRegistrar.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeAccessibilityRegistrar.java
index 010253e..975383e 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeAccessibilityRegistrar.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeAccessibilityRegistrar.java
@@ -19,6 +19,7 @@
 import android.view.View;
 
 import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.RemoteContextAware;
 
 /**
  * Trivial wrapper for calling setAccessibilityDelegate on a View. This exists primarily because the
@@ -31,7 +32,8 @@
             View player, @NonNull CoreDocument coreDocument) {
         return new PlatformRemoteComposeTouchHelper(
                 player,
-                new CoreDocumentAccessibility(coreDocument),
+                new CoreDocumentAccessibility(
+                        coreDocument, ((RemoteContextAware) player).getRemoteContext()),
                 new AndroidPlatformSemanticNodeApplier());
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
index 43118a0..c8474b1 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
@@ -28,6 +28,7 @@
 
 import com.android.internal.widget.ExploreByTouchHelper;
 import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.RemoteContextAware;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
 import com.android.internal.widget.remotecompose.core.semantics.AccessibilitySemantics;
 import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent.Mode;
@@ -55,7 +56,8 @@
             View player, @NonNull CoreDocument coreDocument) {
         return new PlatformRemoteComposeTouchHelper(
                 player,
-                new CoreDocumentAccessibility(coreDocument),
+                new CoreDocumentAccessibility(
+                        coreDocument, ((RemoteContextAware) player).getRemoteContext()),
                 new AndroidPlatformSemanticNodeApplier());
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index f5f4e43..0cfaf55 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -62,7 +62,7 @@
 
     // We also keep a more fine-grained BUILD number, exposed as
     // ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
-    static final float BUILD = 0.3f;
+    static final float BUILD = 0.4f;
 
     @NonNull ArrayList<Operation> mOperations = new ArrayList<>();
 
@@ -860,16 +860,22 @@
      *
      * @param id the click area id
      */
-    public void performClick(int id) {
+    public void performClick(@NonNull RemoteContext context, int id) {
         for (ClickAreaRepresentation clickArea : mClickAreas) {
             if (clickArea.mId == id) {
                 warnClickListeners(clickArea);
                 return;
             }
         }
+
         for (IdActionCallback listener : mIdActionListeners) {
             listener.onAction(id, "");
         }
+
+        Component component = getComponent(id);
+        if (component != null) {
+            component.onClick(context, this, -1, -1);
+        }
     }
 
     /** Warn click listeners when a click area is activated */
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 0b6a3c4..3760af2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 
 import com.android.internal.widget.remotecompose.core.operations.BitmapData;
+import com.android.internal.widget.remotecompose.core.operations.BitmapFontData;
 import com.android.internal.widget.remotecompose.core.operations.ClickArea;
 import com.android.internal.widget.remotecompose.core.operations.ClipPath;
 import com.android.internal.widget.remotecompose.core.operations.ClipRect;
@@ -30,6 +31,7 @@
 import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
 import com.android.internal.widget.remotecompose.core.operations.DrawArc;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
+import com.android.internal.widget.remotecompose.core.operations.DrawBitmapFontText;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
 import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
@@ -45,6 +47,8 @@
 import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
 import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
 import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.FloatFunctionCall;
+import com.android.internal.widget.remotecompose.core.operations.FloatFunctionDefine;
 import com.android.internal.widget.remotecompose.core.operations.Header;
 import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
@@ -147,12 +151,14 @@
     public static final int DATA_BITMAP = 101;
     public static final int DATA_SHADER = 45;
     public static final int DATA_TEXT = 102;
+    public static final int DATA_BITMAP_FONT = 167;
 
     ///////////////////////////// =====================
     public static final int CLIP_PATH = 38;
     public static final int CLIP_RECT = 39;
     public static final int PAINT_VALUES = 40;
     public static final int DRAW_RECT = 42;
+    public static final int DRAW_BITMAP_FONT_TEXT_RUN = 48;
     public static final int DRAW_TEXT_RUN = 43;
     public static final int DRAW_CIRCLE = 46;
     public static final int DRAW_LINE = 47;
@@ -196,11 +202,13 @@
     public static final int PATH_TWEEN = 158;
     public static final int PATH_CREATE = 159;
     public static final int PATH_ADD = 160;
-    public static final int PARTICLE_CREATE = 161;
+    public static final int PARTICLE_DEFINE = 161;
     public static final int PARTICLE_PROCESS = 162;
     public static final int PARTICLE_LOOP = 163;
     public static final int IMPULSE_START = 164;
     public static final int IMPULSE_PROCESS = 165;
+    public static final int FUNCTION_CALL = 166;
+    public static final int FUNCTION_DEFINE = 168;
 
     ///////////////////////////////////////// ======================
 
@@ -276,6 +284,7 @@
         map.put(HEADER, Header::read);
         map.put(DRAW_BITMAP_INT, DrawBitmapInt::read);
         map.put(DATA_BITMAP, BitmapData::read);
+        map.put(DATA_BITMAP_FONT, BitmapFontData::read);
         map.put(DATA_TEXT, TextData::read);
         map.put(THEME, Theme::read);
         map.put(CLICK_AREA, ClickArea::read);
@@ -292,6 +301,7 @@
         map.put(DRAW_ROUND_RECT, DrawRoundRect::read);
         map.put(DRAW_TEXT_ON_PATH, DrawTextOnPath::read);
         map.put(DRAW_TEXT_RUN, DrawText::read);
+        map.put(DRAW_BITMAP_FONT_TEXT_RUN, DrawBitmapFontText::read);
         map.put(DRAW_TWEEN_PATH, DrawTweenPath::read);
         map.put(DATA_PATH, PathData::read);
         map.put(PAINT_VALUES, PaintData::read);
@@ -389,8 +399,10 @@
         map.put(PATH_ADD, PathAppend::read);
         map.put(IMPULSE_START, ImpulseOperation::read);
         map.put(IMPULSE_PROCESS, ImpulseProcess::read);
-        map.put(PARTICLE_CREATE, ParticlesCreate::read);
+        map.put(PARTICLE_DEFINE, ParticlesCreate::read);
         map.put(PARTICLE_LOOP, ParticlesLoop::read);
+        map.put(FUNCTION_CALL, FloatFunctionCall::read);
+        map.put(FUNCTION_DEFINE, FloatFunctionDefine::read);
 
         map.put(ACCESSIBILITY_SEMANTICS, CoreSemantics::read);
         //        map.put(ACCESSIBILITY_CUSTOM_ACTION, CoreSemantics::read);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 1cb8fef..f83ecef 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 
 import com.android.internal.widget.remotecompose.core.operations.BitmapData;
+import com.android.internal.widget.remotecompose.core.operations.BitmapFontData;
 import com.android.internal.widget.remotecompose.core.operations.ClickArea;
 import com.android.internal.widget.remotecompose.core.operations.ClipPath;
 import com.android.internal.widget.remotecompose.core.operations.ClipRect;
@@ -33,6 +34,7 @@
 import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
 import com.android.internal.widget.remotecompose.core.operations.DrawArc;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
+import com.android.internal.widget.remotecompose.core.operations.DrawBitmapFontText;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
 import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
@@ -48,6 +50,8 @@
 import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
 import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
 import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.FloatFunctionCall;
+import com.android.internal.widget.remotecompose.core.operations.FloatFunctionDefine;
 import com.android.internal.widget.remotecompose.core.operations.Header;
 import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
@@ -557,6 +561,18 @@
     }
 
     /**
+     * Records a bitmap font and returns an ID.
+     *
+     * @param glyphs The glyphs that define the bitmap font
+     * @return id of the BitmapFont
+     */
+    public int addBitmapFont(BitmapFontData.Glyph[] glyphs) {
+        int id = mRemoteComposeState.nextId();
+        BitmapFontData.apply(mBuffer, id, glyphs);
+        return id;
+    }
+
+    /**
      * This defines the name of the bitmap given the id.
      *
      * @param id of the Bitmap
@@ -825,6 +841,22 @@
     }
 
     /**
+     * Draw the text with a bitmap font, with origin at (x,y). The origin is interpreted based on
+     * the Align setting in the paint.
+     *
+     * @param textId The text to be drawn
+     * @param bitmapFontId The id of the bitmap font to draw with
+     * @param start The index of the first character in text to draw
+     * @param end (end - 1) is the index of the last character in text to draw
+     * @param x The x-coordinate of the origin of the text being drawn
+     * @param y The y-coordinate of the baseline of the text being drawn
+     */
+    public void addDrawBitmapFontTextRun(
+            int textId, int bitmapFontId, int start, int end, float x, float y) {
+        DrawBitmapFontText.apply(mBuffer, textId, bitmapFontId, start, end, x, y);
+    }
+
+    /**
      * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br>
      * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a
      * way that a panning factor of (0.0, 0.0) would center the text at (x, y)
@@ -1060,6 +1092,14 @@
         return "v1.0";
     }
 
+    /**
+     * Initialize a buffer from a file
+     *
+     * @param path the file path
+     * @param remoteComposeState the associated state
+     * @return the RemoteComposeBuffer
+     * @throws IOException
+     */
     @NonNull
     public static RemoteComposeBuffer fromFile(
             @NonNull String path, @NonNull RemoteComposeState remoteComposeState)
@@ -1134,11 +1174,24 @@
         }
     }
 
+    /**
+     * Read the content of the file into the buffer
+     *
+     * @param file a target file
+     * @param buffer a RemoteComposeBuffer
+     * @throws IOException
+     */
     static void read(@NonNull File file, @NonNull RemoteComposeBuffer buffer) throws IOException {
         FileInputStream fd = new FileInputStream(file);
         read(fd, buffer);
     }
 
+    /**
+     * Initialize a buffer from an input stream
+     *
+     * @param fd the input stream
+     * @param buffer a RemoteComposeBuffer
+     */
     public static void read(@NonNull InputStream fd, @NonNull RemoteComposeBuffer buffer) {
         try {
             byte[] bytes = readAllBytes(fd);
@@ -1150,6 +1203,13 @@
         }
     }
 
+    /**
+     * Load a byte buffer from the input stream
+     *
+     * @param is the input stream
+     * @return a byte buffer containing the input stream content
+     * @throws IOException
+     */
     private static byte[] readAllBytes(@NonNull InputStream is) throws IOException {
         byte[] buff = new byte[32 * 1024]; // moderate size buff to start
         int red = 0;
@@ -1684,7 +1744,27 @@
      * @return id of the color (color ids are short)
      */
     public short addColorExpression(int alpha, float hue, float sat, float value) {
-        ColorExpression c = new ColorExpression(0, alpha, hue, sat, value);
+        ColorExpression c =
+                new ColorExpression(0, ColorExpression.HSV_MODE, alpha, hue, sat, value);
+        short id = (short) mRemoteComposeState.cacheData(c);
+        c.mId = id;
+        c.write(mBuffer);
+        return id;
+    }
+
+    /**
+     * Color calculated by Alpha, Red, Green and Blue. (as floats they can be variables used to
+     * create color transitions)
+     *
+     * @param alpha the alpha value of the color
+     * @param red the red component of the color
+     * @param green the green component of the color
+     * @param blue the blue component of the color
+     * @return id of the color (color ids are short)
+     */
+    public short addColorExpression(float alpha, float red, float green, float blue) {
+        ColorExpression c =
+                new ColorExpression(0, ColorExpression.ARGB_MODE, alpha, red, green, blue);
         short id = (short) mRemoteComposeState.cacheData(c);
         c.mId = id;
         c.write(mBuffer);
@@ -2179,10 +2259,21 @@
                 textAlign);
     }
 
+    /**
+     * Returns the next available id for the given type
+     *
+     * @param type the type of the value
+     * @return a unique id
+     */
     public int createID(int type) {
         return mRemoteComposeState.nextId(type);
     }
 
+    /**
+     * Returns the next available id
+     *
+     * @return a unique id
+     */
     public int nextId() {
         return mRemoteComposeState.nextId();
     }
@@ -2243,4 +2334,27 @@
     public void addParticleLoopEnd() {
         ContainerEnd.apply(mBuffer);
     }
+
+    /**
+     * @param fid The id of the function
+     * @param args The arguments of the function
+     */
+    public void defineFloatFunction(int fid, int[] args) {
+        FloatFunctionDefine.apply(mBuffer, fid, args);
+    }
+
+    /** end the definition of the function */
+    public void addEndFloatFunctionDef() {
+        ContainerEnd.apply(mBuffer);
+    }
+
+    /**
+     * add a function call
+     *
+     * @param id the id of the function to call
+     * @param args the arguments of the function
+     */
+    public void callFloatFunction(int id, float[] args) {
+        FloatFunctionCall.apply(mBuffer, id, args);
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/core/java/com/android/internal/widget/remotecompose/core/RemoteContextAware.java
similarity index 60%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
copy to core/java/com/android/internal/widget/remotecompose/core/RemoteContextAware.java
index aa262f9..bf9a8c0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContextAware.java
@@ -13,15 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.widget.remotecompose.core;
 
-package com.android.wm.shell.flicker.utils
+/**
+ * This interface defines a contract for objects that are aware of a {@link RemoteContext}.
+ *
+ * <p>PlayerViews should implement to provide access to the RemoteContext.
+ */
+public interface RemoteContextAware {
 
-import android.app.Instrumentation
-
-object RecentTasksUtils {
-    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
-        instrumentation.uiAutomation.executeShellCommand(
-            "dumpsys activity service SystemUIService WMShell recents clearAll"
-        )
-    }
-}
\ No newline at end of file
+    /**
+     * Returns the remote context
+     *
+     * @return a RemoteContext
+     */
+    RemoteContext getRemoteContext();
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapFontData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapFontData.java
new file mode 100644
index 0000000..cbd30dc
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapFontData.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2025 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.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** Operation to deal with bitmap font data. */
+public class BitmapFontData extends Operation {
+    private static final int OP_CODE = Operations.DATA_BITMAP_FONT;
+    private static final String CLASS_NAME = "BitmapFontData";
+
+    int mId;
+
+    // Sorted in order of decreasing mChars length.
+    @NonNull Glyph[] mFontGlyphs;
+
+    /**
+     * A bitmap font is comprised of a collection of Glyphs. Note each Glyph has its own bitmap
+     * rather than using a texture atlas.
+     */
+    public static class Glyph {
+        /** The character(s) this glyph represents. */
+        public String mChars;
+
+        /** The id of the bitmap for this glyph, or -1 for space. */
+        public int mBitmapId;
+
+        /** The margin in pixels to the left of the glyph bitmap. */
+        public short mMarginLeft;
+
+        /** The margin in pixels above of the glyph bitmap. */
+        public short mMarginTop;
+
+        /** The margin in pixels to the right of the glyph bitmap. */
+        public short mMarginRight;
+
+        /** The margin in pixels below the glyph bitmap. */
+        public short mMarginBottom;
+
+        public short mBitmapWidth;
+        public short mBitmapHeight;
+
+        public Glyph() {}
+
+        public Glyph(
+                String chars,
+                int bitmapId,
+                short marginLeft,
+                short marginTop,
+                short marginRight,
+                short marginBottom,
+                short width,
+                short height) {
+            mChars = chars;
+            mBitmapId = bitmapId;
+            mMarginLeft = marginLeft;
+            mMarginTop = marginTop;
+            mMarginRight = marginRight;
+            mMarginBottom = marginBottom;
+            mBitmapWidth = width;
+            mBitmapHeight = height;
+        }
+    }
+
+    /**
+     * create a bitmap font structure.
+     *
+     * @param id the id of the bitmap font
+     * @param fontGlyphs the glyphs that define the bitmap font
+     */
+    public BitmapFontData(int id, @NonNull Glyph[] fontGlyphs) {
+        mId = id;
+        mFontGlyphs = fontGlyphs;
+
+        // Sort in order of decreasing mChars length.
+        Arrays.sort(mFontGlyphs, (o1, o2) -> o2.mChars.length() - o1.mChars.length());
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, mId, mFontGlyphs);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "BITMAP FONT DATA " + mId;
+    }
+
+    /**
+     * The name of the class
+     *
+     * @return the name
+     */
+    @NonNull
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    /**
+     * The OP_CODE for this command
+     *
+     * @return the opcode
+     */
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * Add the image to the document
+     *
+     * @param buffer document to write to
+     * @param id the id the bitmap font will be stored under
+     * @param glyphs glyph metadata
+     */
+    public static void apply(@NonNull WireBuffer buffer, int id, @NonNull Glyph[] glyphs) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(id);
+        buffer.writeInt(glyphs.length);
+        for (Glyph element : glyphs) {
+            buffer.writeUTF8(element.mChars);
+            buffer.writeInt(element.mBitmapId);
+            buffer.writeShort(element.mMarginLeft);
+            buffer.writeShort(element.mMarginTop);
+            buffer.writeShort(element.mMarginRight);
+            buffer.writeShort(element.mMarginBottom);
+            buffer.writeShort(element.mBitmapWidth);
+            buffer.writeShort(element.mBitmapHeight);
+        }
+    }
+
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
+    public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+        int id = buffer.readInt();
+        int numGlyphElements = buffer.readInt();
+        Glyph[] glyphs = new Glyph[numGlyphElements];
+        for (int i = 0; i < numGlyphElements; i++) {
+            glyphs[i] = new Glyph();
+            glyphs[i].mChars = buffer.readUTF8();
+            glyphs[i].mBitmapId = buffer.readInt();
+            glyphs[i].mMarginLeft = (short) buffer.readShort();
+            glyphs[i].mMarginTop = (short) buffer.readShort();
+            glyphs[i].mMarginRight = (short) buffer.readShort();
+            glyphs[i].mMarginBottom = (short) buffer.readShort();
+            glyphs[i].mBitmapWidth = (short) buffer.readShort();
+            glyphs[i].mBitmapHeight = (short) buffer.readShort();
+        }
+
+        operations.add(new BitmapFontData(id, glyphs));
+    }
+
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
+    public static void documentation(@NonNull DocumentationBuilder doc) {
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+                .description("Bitmap font data")
+                .field(DocumentedOperation.INT, "id", "id of bitmap font data")
+                .field(INT_ARRAY, "glyphNodes", "list used to greedily convert strings into glyphs")
+                .field(INT_ARRAY, "glyphElements", "");
+    }
+
+    @Override
+    public void apply(@NonNull RemoteContext context) {
+        context.putObject(mId, this);
+    }
+
+    @NonNull
+    @Override
+    public String deepToString(@NonNull String indent) {
+        return indent + toString();
+    }
+
+    /** Finds the largest glyph matching the string at the specified offset, or returns null. */
+    @Nullable
+    public Glyph lookupGlyph(String string, int offset) {
+        // Since mFontGlyphs is sorted on decreasing size, it will match the longest items first.
+        // It is expected that the mFontGlyphs array will be fairly small.
+        for (Glyph glyph : mFontGlyphs) {
+            if (string.startsWith(glyph.mChars, offset)) {
+                return glyph;
+            }
+        }
+        return null;
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
index b385ecd..73f99cc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
@@ -38,7 +38,13 @@
     private static final int OP_CODE = Operations.COLOR_EXPRESSIONS;
     private static final String CLASS_NAME = "ColorExpression";
     public int mId;
+
+    /**
+     * Mode of the color expression 0 = two colors and a tween 1 = color1 is a colorID. 2 color2 is
+     * a colorID. 3 = color1 & color2 are ids 4 = H S V mode 5 = RGB mode 6 = ARGB mode
+     */
     int mMode;
+
     public int mColor1;
     public int mColor2;
     public float mTween = 0.0f;
@@ -51,11 +57,49 @@
     public float mOutValue = 0;
     public int mAlpha = 0xFF; // only used in hsv mode
 
+    private float mArgbAlpha = 0.0f;
+    private float mArgbRed = 0.0f;
+    private float mArgbGreen = 0.0f;
+    private float mArgbBlue = 0.0f;
+
+    private float mOutArgbAlpha = 0.0f;
+    private float mOutArgbRed = 0.0f;
+    private float mOutArgbGreen = 0.0f;
+    private float mOutArgbBlue = 0.0f;
+
     public float mOutTween = 0.0f;
     public int mOutColor1;
     public int mOutColor2;
-    public static final int HSV_MODE = 4;
 
+    /** COLOR_COLOR_INTERPOLATE */
+    public static final byte COLOR_COLOR_INTERPOLATE = 0;
+
+    /** COLOR_ID_INTERPOLATE */
+    public static final byte ID_COLOR_INTERPOLATE = 1;
+
+    /** ID_COLOR_INTERPOLATE */
+    public static final byte COLOR_ID_INTERPOLATE = 2;
+
+    /** ID_ID_INTERPOLATE */
+    public static final byte ID_ID_INTERPOLATE = 3;
+
+    /** H S V mode */
+    public static final byte HSV_MODE = 4;
+
+    /** ARGB mode */
+    public static final byte ARGB_MODE = 5;
+
+    /** ARGB mode with a being an id */
+    public static final byte IDARGB_MODE = 6;
+
+    /**
+     * Create a new ColorExpression object
+     *
+     * @param id the id of the color
+     * @param hue the hue of the color
+     * @param sat the saturation of the color
+     * @param value the value of the color
+     */
     public ColorExpression(int id, float hue, float sat, float value) {
         mMode = HSV_MODE;
         mAlpha = 0xFF;
@@ -67,7 +111,21 @@
         mTween = value;
     }
 
-    public ColorExpression(int id, int alpha, float hue, float sat, float value) {
+    /**
+     * Create a new ColorExpression object based on HSV
+     *
+     * @param id id of the color
+     * @param mode the mode of the color
+     * @param alpha the alpha of the color
+     * @param hue the hue of the color
+     * @param sat the saturation of the color
+     * @param value the value (brightness) of the color
+     */
+    public ColorExpression(int id, byte mode, int alpha, float hue, float sat, float value) {
+        if (mode != HSV_MODE) {
+            throw new RuntimeException("Invalid mode " + mode);
+        }
+        mId = id;
         mMode = HSV_MODE;
         mAlpha = alpha;
         mOutHue = mHue = hue;
@@ -78,6 +136,15 @@
         mTween = value;
     }
 
+    /**
+     * Create a new ColorExpression object based interpolationg two colors
+     *
+     * @param id the id of the color
+     * @param mode the type of mode (are colors ids or actual values)
+     * @param color1 the first color to use
+     * @param color2 the second color to use
+     * @param tween the value to use to interpolate between the two colors
+     */
     public ColorExpression(int id, int mode, int color1, int color2, float tween) {
         this.mId = id;
         this.mMode = mode & 0xFF;
@@ -95,6 +162,28 @@
         this.mOutColor2 = color2;
     }
 
+    /**
+     * Create a new ColorExpression object based on ARGB
+     *
+     * @param id the id of the color
+     * @param mode the mode must be ARGB_MODE
+     * @param alpha the alpha value of the color
+     * @param red the red of component the color
+     * @param green the greej component of the color
+     * @param blue the blue of component the color
+     */
+    public ColorExpression(int id, byte mode, float alpha, float red, float green, float blue) {
+        if (mode != ARGB_MODE) {
+            throw new RuntimeException("Invalid mode " + mode);
+        }
+        mMode = ARGB_MODE;
+        mId = id;
+        mOutArgbAlpha = mArgbAlpha = alpha;
+        mOutArgbRed = mArgbRed = red;
+        mOutArgbGreen = mArgbGreen = green;
+        mOutArgbBlue = mArgbBlue = blue;
+    }
+
     @Override
     public void updateVariables(@NonNull RemoteContext context) {
         if (mMode == 4) {
@@ -108,6 +197,20 @@
                 mOutValue = context.getFloat(Utils.idFromNan(mValue));
             }
         }
+        if (mMode == ARGB_MODE) {
+            if (Float.isNaN(mArgbAlpha)) {
+                mOutArgbAlpha = context.getFloat(Utils.idFromNan(mArgbAlpha));
+            }
+            if (Float.isNaN(mArgbRed)) {
+                mOutArgbRed = context.getFloat(Utils.idFromNan(mArgbRed));
+            }
+            if (Float.isNaN(mArgbGreen)) {
+                mOutArgbGreen = context.getFloat(Utils.idFromNan(mArgbGreen));
+            }
+            if (Float.isNaN(mArgbBlue)) {
+                mOutArgbBlue = context.getFloat(Utils.idFromNan(mArgbBlue));
+            }
+        }
         if (Float.isNaN(mTween)) {
             mOutTween = context.getFloat(Utils.idFromNan(mTween));
         }
@@ -146,13 +249,21 @@
 
     @Override
     public void apply(@NonNull RemoteContext context) {
-        if (mMode == 4) {
+        if (mMode == HSV_MODE) {
             context.loadColor(
                     mId, (mAlpha << 24) | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
             return;
         }
+        if (mMode == ARGB_MODE) {
+            context.loadColor(
+                    mId, Utils.toARGB(mOutArgbAlpha, mOutArgbRed, mOutArgbGreen, mOutArgbBlue));
+            return;
+        }
         if (mOutTween == 0.0) {
-            context.loadColor(mId, mColor1);
+            if ((mMode & 1) == 1) {
+                mOutColor1 = context.getColor(mColor1);
+            }
+            context.loadColor(mId, mOutColor1);
         } else {
             if ((mMode & 1) == 1) {
                 mOutColor1 = context.getColor(mColor1);
@@ -167,14 +278,36 @@
 
     @Override
     public void write(@NonNull WireBuffer buffer) {
-        int mode = mMode | (mAlpha << 16);
-        apply(buffer, mId, mode, mColor1, mColor2, mTween);
+        int mode;
+        switch (mMode) {
+            case ARGB_MODE:
+                apply(buffer, mId, mArgbAlpha, mArgbRed, mArgbGreen, mArgbBlue);
+                break;
+
+            case HSV_MODE:
+                mOutValue = mValue;
+                mColor1 = Float.floatToRawIntBits(mHue);
+                mColor2 = Float.floatToRawIntBits(mSat);
+                mode = mMode | (mAlpha << 16);
+                apply(buffer, mId, mode, mColor1, mColor2, mTween);
+
+                break;
+            case COLOR_ID_INTERPOLATE:
+            case ID_COLOR_INTERPOLATE:
+            case ID_ID_INTERPOLATE:
+            case COLOR_COLOR_INTERPOLATE:
+                apply(buffer, mId, mMode, mColor1, mColor2, mTween);
+
+                break;
+            default:
+                throw new RuntimeException("Invalid mode ");
+        }
     }
 
     @NonNull
     @Override
     public String toString() {
-        if (mMode == 4) {
+        if (mMode == HSV_MODE) {
             return "ColorExpression["
                     + mId
                     + "] = hsv ("
@@ -185,7 +318,20 @@
                     + Utils.floatToString(mValue)
                     + ")";
         }
-
+        Utils.log(" ColorExpression toString" + mId + " " + mMode);
+        if (mMode == ARGB_MODE) {
+            return "ColorExpression["
+                    + mId
+                    + "] = rgb ("
+                    + Utils.floatToString(mArgbAlpha)
+                    + ", "
+                    + Utils.floatToString(mArgbRed)
+                    + ", "
+                    + Utils.floatToString(mArgbGreen)
+                    + ", "
+                    + Utils.floatToString(mArgbRed)
+                    + ")";
+        }
         String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1);
         String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2);
         return "ColorExpression["
@@ -230,12 +376,38 @@
      */
     public static void apply(
             @NonNull WireBuffer buffer, int id, int mode, int color1, int color2, float tween) {
+        apply(buffer, id, mode, color1, color2, Float.floatToRawIntBits(tween));
+    }
+
+    /**
+     * Call to write a ColorExpression object on the buffer
+     *
+     * @param buffer
+     * @param id of the ColorExpression object
+     * @param alpha
+     * @param red
+     * @param green
+     * @param blue
+     */
+    public static void apply(
+            @NonNull WireBuffer buffer, int id, float alpha, float red, float green, float blue) {
+        int param1 = (Float.isNaN(alpha)) ? IDARGB_MODE : ARGB_MODE;
+        param1 |=
+                (Float.isNaN(alpha)) ? Utils.idFromNan(alpha) << 16 : ((int) (alpha * 1024)) << 16;
+        int param2 = Float.floatToRawIntBits(red);
+        int param3 = Float.floatToRawIntBits(green);
+        int param4 = Float.floatToRawIntBits(blue);
+        apply(buffer, id, param1, param2, param3, param4);
+    }
+
+    private static void apply(
+            @NonNull WireBuffer buffer, int id, int param1, int param2, int param3, int param4) {
         buffer.start(OP_CODE);
         buffer.writeInt(id);
-        buffer.writeInt(mode);
-        buffer.writeInt(color1);
-        buffer.writeInt(color2);
-        buffer.writeFloat(tween);
+        buffer.writeInt(param1);
+        buffer.writeInt(param2);
+        buffer.writeInt(param3);
+        buffer.writeInt(param4);
     }
 
     /**
@@ -246,12 +418,48 @@
      */
     public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
         int id = buffer.readInt();
-        int mode = buffer.readInt();
-        int color1 = buffer.readInt();
-        int color2 = buffer.readInt();
-        float tween = buffer.readFloat();
-
-        operations.add(new ColorExpression(id, mode, color1, color2, tween));
+        int param1 = buffer.readInt();
+        int param2 = buffer.readInt();
+        int param3 = buffer.readInt();
+        int param4 = buffer.readInt();
+        int mode = param1 & 0xFF;
+        float alpha;
+        float red;
+        float green;
+        float blue;
+        switch (mode) {
+            case IDARGB_MODE:
+                alpha = Utils.asNan(param1 >> 16);
+                red = Float.intBitsToFloat(param2);
+                green = Float.intBitsToFloat(param3);
+                blue = Float.intBitsToFloat(param4);
+                operations.add(new ColorExpression(id, (byte) ARGB_MODE, alpha, red, green, blue));
+                break;
+            case ARGB_MODE:
+                alpha = (param1 >> 16) / 1024.0f;
+                red = Float.intBitsToFloat(param2);
+                green = Float.intBitsToFloat(param3);
+                blue = Float.intBitsToFloat(param4);
+                operations.add(new ColorExpression(id, (byte) ARGB_MODE, alpha, red, green, blue));
+                break;
+            case HSV_MODE:
+                alpha = (param1 >> 16) / 1024.0f;
+                float hue = Float.intBitsToFloat(param2);
+                float sat = Float.intBitsToFloat(param3);
+                float value = Float.intBitsToFloat(param4);
+                operations.add(new ColorExpression(id, HSV_MODE, (param1 >> 16), hue, sat, value));
+                break;
+            case COLOR_ID_INTERPOLATE:
+            case ID_COLOR_INTERPOLATE:
+            case ID_ID_INTERPOLATE:
+            case COLOR_COLOR_INTERPOLATE:
+                operations.add(
+                        new ColorExpression(
+                                id, mode, param2, param3, Float.intBitsToFloat(param4)));
+                break;
+            default:
+                throw new RuntimeException("Invalid mode " + mode);
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
index 7f1ba6f..411353b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -31,8 +31,8 @@
 /** Base class for commands that take 3 float */
 public abstract class DrawBase2 extends PaintOperation implements VariableSupport {
     @NonNull protected String mName = "DrawRectBase";
-    protected float mV1;
-    protected float mV2;
+    float mV1;
+    float mV2;
     float mValue1;
     float mValue2;
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapFontText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapFontText.java
new file mode 100644
index 0000000..258988e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapFontText.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2025 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.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.List;
+
+/** Draw Text */
+public class DrawBitmapFontText extends PaintOperation implements VariableSupport {
+    private static final int OP_CODE = Operations.DRAW_BITMAP_FONT_TEXT_RUN;
+    private static final String CLASS_NAME = "DrawBitmapFontText";
+    int mTextID;
+    int mBitmapFontID;
+    int mStart;
+    int mEnd;
+    float mX;
+    float mY;
+    float mOutX;
+    float mOutY;
+
+    public DrawBitmapFontText(int textID, int bitmapFontID, int start, int end, float x, float y) {
+        mTextID = textID;
+        mBitmapFontID = bitmapFontID;
+        mStart = start;
+        mEnd = end;
+        mOutX = mX = x;
+        mOutY = mY = y;
+    }
+
+    @Override
+    public void updateVariables(@NonNull RemoteContext context) {
+        mOutX = Float.isNaN(mX) ? context.getFloat(Utils.idFromNan(mX)) : mX;
+        mOutY = Float.isNaN(mY) ? context.getFloat(Utils.idFromNan(mY)) : mY;
+    }
+
+    @Override
+    public void registerListening(@NonNull RemoteContext context) {
+        if (Float.isNaN(mX)) {
+            context.listensTo(Utils.idFromNan(mX), this);
+        }
+        if (Float.isNaN(mY)) {
+            context.listensTo(Utils.idFromNan(mY), this);
+        }
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, mTextID, mBitmapFontID, mStart, mEnd, mX, mY);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "DrawBitmapFontText ["
+                + mTextID
+                + "] "
+                + mBitmapFontID
+                + ", "
+                + mStart
+                + ", "
+                + mEnd
+                + ", "
+                + floatToString(mX, mOutX)
+                + ", "
+                + floatToString(mY, mOutY);
+    }
+
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
+    public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+        int text = buffer.readInt();
+        int bitmapFont = buffer.readInt();
+        int start = buffer.readInt();
+        int end = buffer.readInt();
+        float x = buffer.readFloat();
+        float y = buffer.readFloat();
+        DrawBitmapFontText op = new DrawBitmapFontText(text, bitmapFont, start, end, x, y);
+
+        operations.add(op);
+    }
+
+    /**
+     * The name of the class
+     *
+     * @return the name
+     */
+    @NonNull
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    /**
+     * The OP_CODE for this command
+     *
+     * @return the opcode
+     */
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * Writes out the operation to the buffer
+     *
+     * @param buffer write the command to the buffer
+     * @param textID id of the text
+     * @param bitmapFontID id of the bitmap font
+     * @param start Start position
+     * @param end end position
+     * @param x position of where to draw
+     * @param y position of where to draw
+     */
+    public static void apply(
+            @NonNull WireBuffer buffer,
+            int textID,
+            int bitmapFontID,
+            int start,
+            int end,
+            float x,
+            float y) {
+        buffer.start(Operations.DRAW_BITMAP_FONT_TEXT_RUN);
+        buffer.writeInt(textID);
+        buffer.writeInt(bitmapFontID);
+        buffer.writeInt(start);
+        buffer.writeInt(end);
+        buffer.writeFloat(x);
+        buffer.writeFloat(y);
+    }
+
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
+    public static void documentation(@NonNull DocumentationBuilder doc) {
+        doc.operation("Draw Operations", id(), CLASS_NAME)
+                .description("Draw a run of bitmap font text, all in a single direction")
+                .field(DocumentedOperation.INT, "textId", "id of bitmap")
+                .field(DocumentedOperation.INT, "bitmapFontId", "id of the bitmap font")
+                .field(
+                        DocumentedOperation.INT,
+                        "start",
+                        "The start of the text to render. -1=end of string")
+                .field(DocumentedOperation.INT, "end", "The end of the text to render")
+                .field(
+                        DocumentedOperation.INT,
+                        "contextStart",
+                        "the index of the start of the shaping context")
+                .field(
+                        DocumentedOperation.INT,
+                        "contextEnd",
+                        "the index of the end of the shaping context")
+                .field(DocumentedOperation.FLOAT, "x", "The x position at which to draw the text")
+                .field(DocumentedOperation.FLOAT, "y", "The y position at which to draw the text")
+                .field(DocumentedOperation.BOOLEAN, "RTL", "Whether the run is in RTL direction");
+    }
+
+    @Override
+    public void paint(@NonNull PaintContext context) {
+        RemoteContext remoteContext = context.getContext();
+        String textToPaint = remoteContext.getText(mTextID);
+        if (textToPaint == null) {
+            return;
+        }
+        if (mEnd == -1) {
+            if (mStart != 0) {
+                textToPaint = textToPaint.substring(mStart);
+            }
+        } else if (mEnd > textToPaint.length()) {
+            textToPaint = textToPaint.substring(mStart);
+        } else {
+            textToPaint = textToPaint.substring(mStart, mEnd);
+        }
+
+        BitmapFontData bitmapFont = (BitmapFontData) remoteContext.getObject(mBitmapFontID);
+        if (bitmapFont == null) {
+            return;
+        }
+
+        float xPos = mX;
+        int pos = 0;
+        while (pos < textToPaint.length()) {
+            BitmapFontData.Glyph glyph = bitmapFont.lookupGlyph(textToPaint, pos);
+            if (glyph == null) {
+                pos++;
+                continue;
+            }
+
+            pos += glyph.mChars.length();
+            if (glyph.mBitmapId == -1) {
+                // Space is represented by a glyph of -1.
+                xPos += glyph.mMarginLeft + glyph.mMarginRight;
+                continue;
+            }
+
+            xPos += glyph.mMarginLeft;
+            float xPos2 = xPos + glyph.mBitmapWidth;
+            context.drawBitmap(
+                    glyph.mBitmapId, xPos, mY + glyph.mMarginTop, xPos2, mY + glyph.mBitmapHeight);
+            xPos = xPos2 + glyph.mMarginRight;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionCall.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionCall.java
new file mode 100644
index 0000000..eccc00a
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionCall.java
@@ -0,0 +1,185 @@
+/*
+ * 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.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** This provides the command to call a floatfunction defined in floatfunction */
+public class FloatFunctionCall extends PaintOperation implements VariableSupport {
+    private static final int OP_CODE = Operations.FUNCTION_CALL;
+    private static final String CLASS_NAME = "FunctionCall";
+    private final int mId;
+    private final float[] mArgs;
+    private final float[] mOutArgs;
+
+    FloatFunctionDefine mFunction;
+
+    @NonNull private ArrayList<Operation> mList = new ArrayList<>();
+
+    @NonNull AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+
+    /**
+     * Create a new FloatFunctionCall operation
+     *
+     * @param id The function to call
+     * @param args the arguments to call it with
+     */
+    public FloatFunctionCall(int id, float[] args) {
+        mId = id;
+        mArgs = args;
+        if (args != null) {
+            mOutArgs = new float[args.length];
+            System.arraycopy(args, 0, mOutArgs, 0, args.length);
+        } else {
+            mOutArgs = null;
+        }
+    }
+
+    @Override
+    public void updateVariables(@NonNull RemoteContext context) {
+        if (mOutArgs != null) {
+            for (int i = 0; i < mArgs.length; i++) {
+                float v = mArgs[i];
+                mOutArgs[i] =
+                        (Float.isNaN(v)
+                                        && !AnimatedFloatExpression.isMathOperator(v)
+                                        && !NanMap.isDataVariable(v))
+                                ? context.getFloat(Utils.idFromNan(v))
+                                : v;
+            }
+        }
+    }
+
+    @Override
+    public void registerListening(@NonNull RemoteContext context) {
+        mFunction = (FloatFunctionDefine) context.getObject(mId);
+        if (mArgs != null) {
+            for (int i = 0; i < mArgs.length; i++) {
+                float v = mArgs[i];
+                if (Float.isNaN(v)
+                        && !AnimatedFloatExpression.isMathOperator(v)
+                        && !NanMap.isDataVariable(v)) {
+                    context.listensTo(Utils.idFromNan(v), this);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, mId, mArgs);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        String str = "callFunction[" + Utils.idString(mId) + "] ";
+        for (int i = 0; i < mArgs.length; i++) {
+            str += ((i == 0) ? "" : " ,") + Utils.floatToString(mArgs[i], mOutArgs[i]);
+        }
+        return str;
+    }
+
+    /**
+     * Write the operation on the buffer
+     *
+     * @param buffer the buffer to write to
+     * @param id the id of the function to call
+     * @param args the arguments to call the function with
+     */
+    public static void apply(@NonNull WireBuffer buffer, int id, @Nullable float[] args) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(id);
+        if (args != null) {
+            buffer.writeInt(args.length);
+            for (int i = 0; i < args.length; i++) {
+                buffer.writeFloat(args[i]);
+            }
+        } else {
+            buffer.writeInt(0);
+        }
+    }
+
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
+    public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+        int id = buffer.readInt();
+        int argLen = buffer.readInt();
+        float[] args = null;
+        if (argLen > 0) {
+            args = new float[argLen];
+            for (int i = 0; i < argLen; i++) {
+                args[i] = buffer.readFloat();
+            }
+        }
+
+        FloatFunctionCall data = new FloatFunctionCall(id, args);
+        operations.add(data);
+    }
+
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
+    public static void documentation(@NonNull DocumentationBuilder doc) {
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+                .description("Command to call the function")
+                .field(DocumentedOperation.INT, "id", "id of function to call")
+                .field(INT, "argLen", "the number of Arguments")
+                .field(FLOAT_ARRAY, "values", "argLen", "array of float arguments");
+    }
+
+    @NonNull
+    @Override
+    public String deepToString(@NonNull String indent) {
+        return indent + toString();
+    }
+
+    @Override
+    public void paint(@NonNull PaintContext context) {
+        RemoteContext remoteContext = context.getContext();
+        int[] args = mFunction.getArgs();
+        for (int j = 0; j < mOutArgs.length; j++) {
+            remoteContext.loadFloat(args[j], mOutArgs[j]);
+            updateVariables(remoteContext);
+        }
+        mFunction.execute(remoteContext);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionDefine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionDefine.java
new file mode 100644
index 0000000..efd4eec
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionDefine.java
@@ -0,0 +1,168 @@
+/*
+ * 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.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.Container;
+import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This defines a function Operator. It contains a collection of commands which are then executed by
+ * the FloatFunctionCall command
+ */
+public class FloatFunctionDefine extends Operation implements VariableSupport, Container {
+    private static final int OP_CODE = Operations.FUNCTION_DEFINE;
+    private static final String CLASS_NAME = "FunctionDefine";
+    private final int mId;
+    private final int[] mFloatVarId;
+    @NonNull private ArrayList<Operation> mList = new ArrayList<>();
+
+    @NonNull AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+
+    /**
+     * @param id The id of the function
+     * @param floatVarId the ids of the variables
+     */
+    public FloatFunctionDefine(int id, int[] floatVarId) {
+        mId = id;
+        mFloatVarId = floatVarId;
+    }
+
+    @NonNull
+    @Override
+    public ArrayList<Operation> getList() {
+        return mList;
+    }
+
+    @Override
+    public void updateVariables(@NonNull RemoteContext context) {}
+
+    @Override
+    public void registerListening(@NonNull RemoteContext context) {
+        context.putObject(mId, this);
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, mId, mFloatVarId);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        String str = "FloatFunctionDefine[" + Utils.idString(mId) + "] (";
+        for (int j = 0; j < mFloatVarId.length; j++) {
+            str += "[" + mFloatVarId[j] + "] ";
+        }
+        str += ")";
+        for (Operation operation : mList) {
+            str += " \n  " + operation.toString();
+        }
+        return str;
+    }
+
+    /**
+     * Write the operation on the buffer
+     *
+     * @param buffer the buffer to write to
+     * @param id the id of the function
+     * @param varId the ids of the variables
+     */
+    public static void apply(@NonNull WireBuffer buffer, int id, @NonNull int[] varId) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(id);
+        buffer.writeInt(varId.length);
+        for (int i = 0; i < varId.length; i++) {
+            buffer.writeInt(varId[i]);
+        }
+    }
+
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
+    public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+        int id = buffer.readInt();
+        int varLen = buffer.readInt();
+        int[] varId = new int[varLen];
+        for (int i = 0; i < varId.length; i++) {
+            varId[i] = buffer.readInt();
+        }
+        FloatFunctionDefine data = new FloatFunctionDefine(id, varId);
+        operations.add(data);
+    }
+
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
+    public static void documentation(@NonNull DocumentationBuilder doc) {
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+                .description("Define a function")
+                .field(DocumentedOperation.INT, "id", "The reference of the function")
+                .field(INT, "varLen", "number of arguments to the function")
+                .field(FLOAT_ARRAY, "id", "varLen", "id equations");
+    }
+
+    @NonNull
+    @Override
+    public String deepToString(@NonNull String indent) {
+        return indent + toString();
+    }
+
+    /**
+     * @return the array of id's
+     */
+    public int[] getArgs() {
+        return mFloatVarId;
+    }
+
+    @Override
+    public void apply(@NonNull RemoteContext context) {}
+
+    /**
+     * Execute the function by applying the list of operations
+     *
+     * @param context the current RemoteContext
+     */
+    public void execute(@NonNull RemoteContext context) {
+        for (Operation op : mList) {
+            if (op instanceof VariableSupport) {
+                ((VariableSupport) op).updateVariables(context);
+            }
+
+            context.incrementOpCount();
+            op.apply(context);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
index 9e891c4..ee9e7a4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
@@ -39,7 +39,7 @@
  * for constructing the particles
  */
 public class ParticlesCreate extends Operation implements VariableSupport {
-    private static final int OP_CODE = Operations.PARTICLE_CREATE;
+    private static final int OP_CODE = Operations.PARTICLE_DEFINE;
     private static final String CLASS_NAME = "ParticlesCreate";
     private final int mId;
     private final float[][] mEquations;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java
index 7910790..8d19c94 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java
@@ -38,7 +38,7 @@
 import java.util.List;
 
 /**
- * This provides the mechinism to evolve the particles It consist of a restart equation and a list
+ * This provides the mechanism to evolve the particles It consist of a restart equation and a list
  * of equations particle restarts if restart equation > 0
  */
 public class ParticlesLoop extends PaintOperation implements VariableSupport, Container {
@@ -159,10 +159,10 @@
     /**
      * Write the operation on the buffer
      *
-     * @param buffer
-     * @param id
-     * @param restart
-     * @param equations
+     * @param buffer the buffer to write to
+     * @param id the id of the particle system
+     * @param restart the restart equation
+     * @param equations the equations to evolve the particles
      */
     public static void apply(
             @NonNull WireBuffer buffer,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index bd68d5a..5f50540 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -289,4 +289,21 @@
         }
         return 0;
     }
+
+    /**
+     * Convert float alpha, red,g reen, blue to ARGB int
+     *
+     * @param alpha alpha value
+     * @param red red value
+     * @param green green value
+     * @param blue blue value
+     * @return ARGB int
+     */
+    public static int toARGB(float alpha, float red, float green, float blue) {
+        int a = (int) (alpha * 255.0f + 0.5f);
+        int r = (int) (red * 255.0f + 0.5f);
+        int g = (int) (green * 255.0f + 0.5f);
+        int b = (int) (blue * 255.0f + 0.5f);
+        return (a << 24 | r << 16 | g << 8 | b);
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index 8e733ce..96a31ae 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -37,13 +37,15 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
 
 import java.util.ArrayList;
 import java.util.HashSet;
 
 /** Generic Component class */
 public class Component extends PaintOperation
-        implements Container, Measurable, SerializableToString {
+        implements Container, Measurable, SerializableToString, Serializable {
 
     private static final boolean DEBUG = false;
 
@@ -61,7 +63,7 @@
     public boolean mNeedsMeasure = true;
     public boolean mNeedsRepaint = false;
     @Nullable public AnimateMeasure mAnimateMeasure;
-    @NonNull public AnimationSpec mAnimationSpec = new AnimationSpec();
+    @NonNull public AnimationSpec mAnimationSpec = AnimationSpec.DEFAULT;
     public boolean mFirstLayout = true;
     @NonNull PaintBundle mPaint = new PaintBundle();
     @NonNull protected HashSet<ComponentValue> mComponentValues = new HashSet<>();
@@ -318,6 +320,14 @@
         }
     }
 
+    protected AnimationSpec getAnimationSpec() {
+        return mAnimationSpec;
+    }
+
+    protected void setAnimationSpec(@NonNull AnimationSpec animationSpec) {
+        mAnimationSpec = animationSpec;
+    }
+
     public enum Visibility {
         GONE,
         VISIBLE,
@@ -501,16 +511,17 @@
      *
      * @param context
      * @param document
-     * @param x
-     * @param y
+     * @param x x location on screen or -1 if unconditional click
+     * @param y y location on screen or -1 if unconditional click
      */
     public void onClick(
             @NonNull RemoteContext context, @NonNull CoreDocument document, float x, float y) {
-        if (!contains(x, y)) {
+        boolean isUnconditional = x == -1 & y == -1;
+        if (!isUnconditional && !contains(x, y)) {
             return;
         }
-        float cx = x - getScrollX();
-        float cy = y - getScrollY();
+        float cx = isUnconditional ? -1 : x - getScrollX();
+        float cy = isUnconditional ? -1 : y - getScrollY();
         for (Operation op : mList) {
             if (op instanceof Component) {
                 ((Component) op).onClick(context, document, cx, cy);
@@ -1035,4 +1046,15 @@
         }
         return null;
     }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        serializer.add("type", getSerializedName());
+        serializer.add("id", mComponentId);
+        serializer.add("x", mX);
+        serializer.add("y", mY);
+        serializer.add("width", mWidth);
+        serializer.add("height", mHeight);
+        serializer.add("visibility", mVisibility);
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index dcd3348..c517e50 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -32,6 +32,7 @@
 import com.android.internal.widget.remotecompose.core.operations.PaintData;
 import com.android.internal.widget.remotecompose.core.operations.TextData;
 import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
+import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DimensionModifierOperation;
@@ -44,6 +45,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 
 import java.util.ArrayList;
 
@@ -234,6 +236,8 @@
                 mZIndexModifier = (ZIndexModifierOperation) op;
             } else if (op instanceof GraphicsLayerModifierOperation) {
                 mGraphicsLayerModifier = (GraphicsLayerModifierOperation) op;
+            } else if (op instanceof AnimationSpec) {
+                mAnimationSpec = (AnimationSpec) op;
             } else if (op instanceof ScrollDelegate) {
                 ScrollDelegate scrollDelegate = (ScrollDelegate) op;
                 if (scrollDelegate.handlesHorizontalScroll()) {
@@ -256,6 +260,16 @@
         if (heightInConstraints != null) {
             mHeightModifier.setHeightIn(heightInConstraints);
         }
+
+        if (mAnimationSpec != AnimationSpec.DEFAULT) {
+            for (int i = 0; i < mChildrenComponents.size(); i++) {
+                Component c = mChildrenComponents.get(i);
+                if (c != null && c.getAnimationSpec() == AnimationSpec.DEFAULT) {
+                    c.setAnimationSpec(mAnimationSpec);
+                }
+            }
+        }
+
         setWidth(computeModifierDefinedWidth(null));
         setHeight(computeModifierDefinedHeight(null));
     }
@@ -473,4 +487,14 @@
     public ArrayList<Component> getChildrenComponents() {
         return mChildrenComponents;
     }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        super.serialize(serializer);
+        serializer.add("children", mChildrenComponents);
+        serializer.add("paddingLeft", mPaddingLeft);
+        serializer.add("paddingRight", mPaddingRight);
+        serializer.add("paddingTop", mPaddingTop);
+        serializer.add("paddingBottom", mPaddingBottom);
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
index 4977a15..a4e8f5c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
@@ -94,6 +94,11 @@
         return "TouchCancelModifier";
     }
 
+    /**
+     * Write the operation on the buffer
+     *
+     * @param buffer a WireBuffer
+     */
     public static void apply(WireBuffer buffer) {
         buffer.start(OP_CODE);
     }
@@ -108,6 +113,11 @@
         operations.add(new TouchCancelModifierOperation());
     }
 
+    /**
+     * Add documentation for this operation
+     *
+     * @param doc a DocumentationBuilder
+     */
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Modifier Operations", OP_CODE, name())
                 .description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java
index 8c51f2e..6191bf4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java
@@ -96,14 +96,30 @@
         return "TouchModifier";
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     */
     public static void apply(WireBuffer buffer) {
         buffer.start(OP_CODE);
     }
 
+    /**
+     * Read the operation from the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param operations the list of operations we read so far
+     */
     public static void read(WireBuffer buffer, List<Operation> operations) {
         operations.add(new TouchDownModifierOperation());
     }
 
+    /**
+     * Add documentation for this operation
+     *
+     * @param doc a DocumentationBuilder
+     */
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Modifier Operations", OP_CODE, name())
                 .description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java
index a12c356..a7e423e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java
@@ -94,14 +94,30 @@
         return "TouchUpModifier";
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     */
     public static void apply(WireBuffer buffer) {
         buffer.start(OP_CODE);
     }
 
+    /**
+     * Read the operation from the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param operations the list of operations we read so far
+     */
     public static void read(WireBuffer buffer, List<Operation> operations) {
         operations.add(new TouchUpModifierOperation());
     }
 
+    /**
+     * Add documentation for this operation
+     *
+     * @param doc a DocumentationBuilder
+     */
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Modifier Operations", OP_CODE, name())
                 .description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
index d3b3e0e..e5cd485 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
@@ -38,8 +38,8 @@
     private final @NonNull Component mComponent;
     private final @NonNull ComponentMeasure mOriginal;
     private final @NonNull ComponentMeasure mTarget;
-    private int mDuration;
-    private int mDurationVisibilityChange = mDuration;
+    private float mDuration;
+    private float mDurationVisibilityChange = mDuration;
     private @NonNull AnimationSpec.ANIMATION mEnterAnimation = AnimationSpec.ANIMATION.FADE_IN;
     private @NonNull AnimationSpec.ANIMATION mExitAnimation = AnimationSpec.ANIMATION.FADE_OUT;
     private int mMotionEasingType = GeneralEasing.CUBIC_STANDARD;
@@ -64,8 +64,8 @@
             @NonNull Component component,
             @NonNull ComponentMeasure original,
             @NonNull ComponentMeasure target,
-            int duration,
-            int durationVisibilityChange,
+            float duration,
+            float durationVisibilityChange,
             @NonNull AnimationSpec.ANIMATION enterAnimation,
             @NonNull AnimationSpec.ANIMATION exitAnimation,
             int motionEasingType,
@@ -94,6 +94,11 @@
         component.mVisibility = target.getVisibility();
     }
 
+    /**
+     * Update the current bounds/visibility/etc given the current time
+     *
+     * @param currentTime the time we use to evaluate the animation
+     */
     public void update(long currentTime) {
         long elapsed = currentTime - mStartTime;
         float motionProgress = elapsed / (float) mDuration;
@@ -347,6 +352,11 @@
         return mOriginal.getH() * (1 - mP) + mTarget.getH() * mP;
     }
 
+    /**
+     * Returns the visibility for this measure
+     *
+     * @return the current visibility (possibly interpolated)
+     */
     public float getVisibility() {
         if (mOriginal.getVisibility() == mTarget.getVisibility()) {
             return 1f;
@@ -357,6 +367,12 @@
         }
     }
 
+    /**
+     * Set the target values from the given measure
+     *
+     * @param measure the target measure
+     * @param currentTime the current time
+     */
     public void updateTarget(@NonNull ComponentMeasure measure, long currentTime) {
         mOriginal.setX(getX());
         mOriginal.setY(getY());
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
index 6dff4a8..6e9de58 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
@@ -24,25 +24,28 @@
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 import com.android.internal.widget.remotecompose.core.operations.utilities.easing.GeneralEasing;
 
 import java.util.List;
 
 /** Basic component animation spec */
-public class AnimationSpec extends Operation {
+public class AnimationSpec extends Operation implements ModifierOperation {
+    public static final AnimationSpec DEFAULT = new AnimationSpec();
     int mAnimationId = -1;
-    int mMotionDuration = 300;
+    float mMotionDuration = 300;
     int mMotionEasingType = GeneralEasing.CUBIC_STANDARD;
-    int mVisibilityDuration = 300;
+    float mVisibilityDuration = 300;
     int mVisibilityEasingType = GeneralEasing.CUBIC_STANDARD;
     @NonNull ANIMATION mEnterAnimation = ANIMATION.FADE_IN;
     @NonNull ANIMATION mExitAnimation = ANIMATION.FADE_OUT;
 
     public AnimationSpec(
             int animationId,
-            int motionDuration,
+            float motionDuration,
             int motionEasingType,
-            int visibilityDuration,
+            float visibilityDuration,
             int visibilityEasingType,
             @NonNull ANIMATION enterAnimation,
             @NonNull ANIMATION exitAnimation) {
@@ -70,7 +73,7 @@
         return mAnimationId;
     }
 
-    public int getMotionDuration() {
+    public float getMotionDuration() {
         return mMotionDuration;
     }
 
@@ -78,7 +81,7 @@
         return mMotionEasingType;
     }
 
-    public int getVisibilityDuration() {
+    public float getVisibilityDuration() {
         return mVisibilityDuration;
     }
 
@@ -102,6 +105,25 @@
         return "ANIMATION_SPEC (" + mMotionDuration + " ms)";
     }
 
+    @Override
+    public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+        serializer.append(
+                indent,
+                "ANIMATION_SPEC = ["
+                        + getMotionDuration()
+                        + ", "
+                        + getMotionEasingType()
+                        + ", "
+                        + getVisibilityDuration()
+                        + ", "
+                        + getVisibilityEasingType()
+                        + ", "
+                        + getEnterAnimation()
+                        + ", "
+                        + getExitAnimation()
+                        + "]");
+    }
+
     public enum ANIMATION {
         FADE_IN,
         FADE_OUT,
@@ -156,10 +178,22 @@
         return Operations.ANIMATION_SPEC;
     }
 
+    /**
+     * Returns an int for the given ANIMATION
+     *
+     * @param animation an ANIMATION enum value
+     * @return a corresponding int value
+     */
     public static int animationToInt(@NonNull ANIMATION animation) {
         return animation.ordinal();
     }
 
+    /**
+     * Maps int value to the corresponding ANIMATION enum values
+     *
+     * @param value int value mapped to the enum
+     * @return the corresponding ANIMATION enum value
+     */
     @NonNull
     public static ANIMATION intToAnimation(int value) {
         switch (value) {
@@ -184,20 +218,32 @@
         }
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param animationId the animation id
+     * @param motionDuration the duration of the motion animation
+     * @param motionEasingType the type of easing for the motion animation
+     * @param visibilityDuration the duration of the visibility animation
+     * @param visibilityEasingType the type of easing for the visibility animation
+     * @param enterAnimation the type of animation when "entering" (newly visible)
+     * @param exitAnimation the type of animation when "exiting" (newly gone)
+     */
     public static void apply(
             @NonNull WireBuffer buffer,
             int animationId,
-            int motionDuration,
+            float motionDuration,
             int motionEasingType,
-            int visibilityDuration,
+            float visibilityDuration,
             int visibilityEasingType,
             @NonNull ANIMATION enterAnimation,
             @NonNull ANIMATION exitAnimation) {
         buffer.start(Operations.ANIMATION_SPEC);
         buffer.writeInt(animationId);
-        buffer.writeInt(motionDuration);
+        buffer.writeFloat(motionDuration);
         buffer.writeInt(motionEasingType);
-        buffer.writeInt(visibilityDuration);
+        buffer.writeFloat(visibilityDuration);
         buffer.writeInt(visibilityEasingType);
         buffer.writeInt(animationToInt(enterAnimation));
         buffer.writeInt(animationToInt(exitAnimation));
@@ -211,9 +257,9 @@
      */
     public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
         int animationId = buffer.readInt();
-        int motionDuration = buffer.readInt();
+        float motionDuration = buffer.readFloat();
         int motionEasingType = buffer.readInt();
-        int visibilityDuration = buffer.readInt();
+        float visibilityDuration = buffer.readFloat();
         int visibilityEasingType = buffer.readInt();
         ANIMATION enterAnimation = intToAnimation(buffer.readInt());
         ANIMATION exitAnimation = intToAnimation(buffer.readInt());
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
index 64e2f004..051579b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
@@ -30,6 +30,15 @@
 
     @NonNull PaintBundle mPaint = new PaintBundle();
 
+    /**
+     * Animate the particle animation
+     *
+     * @param context the current paint context
+     * @param component the target component
+     * @param start the component's measure at the end of the animation
+     * @param end the component's measure at the end of the animation
+     * @param progress the current animation progress
+     */
     public void animate(
             @NonNull PaintContext context,
             @NonNull Component component,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
index a37f35f..35d639e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
@@ -29,6 +29,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 
 import java.util.List;
 
@@ -191,6 +192,15 @@
         return Operations.LAYOUT_BOX;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param componentId the component id
+     * @param animationId the component animation id
+     * @param horizontalPositioning the horizontal positioning rules
+     * @param verticalPositioning the vertical positioning rules
+     */
     public static void apply(
             @NonNull WireBuffer buffer,
             int componentId,
@@ -260,4 +270,28 @@
     public void write(@NonNull WireBuffer buffer) {
         apply(buffer, mComponentId, mAnimationId, mHorizontalPositioning, mVerticalPositioning);
     }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        super.serialize(serializer);
+        serializer.add("verticalPositioning", getPositioningString(mVerticalPositioning));
+        serializer.add("horizontalPositioning", getPositioningString(mHorizontalPositioning));
+    }
+
+    private String getPositioningString(int pos) {
+        switch (pos) {
+            case START:
+                return "START";
+            case CENTER:
+                return "CENTER";
+            case END:
+                return "END";
+            case TOP:
+                return "TOP";
+            case BOTTOM:
+                return "BOTTOM";
+            default:
+                return "NONE";
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
index 0091a47..8448132 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
@@ -28,6 +28,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 
 import java.util.List;
 
@@ -91,6 +92,13 @@
         return Operations.LAYOUT_CANVAS;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param componentId the component id
+     * @param animationId the component animation id
+     */
     public static void apply(@NonNull WireBuffer buffer, int componentId, int animationId) {
         buffer.start(Operations.LAYOUT_CANVAS);
         buffer.writeInt(componentId);
@@ -142,4 +150,27 @@
     public void write(@NonNull WireBuffer buffer) {
         apply(buffer, mComponentId, mAnimationId);
     }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        super.serialize(serializer);
+        serializer.add("", mHorizontalPositioning);
+    }
+
+    private String getPositioningString(int pos) {
+        switch (pos) {
+            case START:
+                return "START";
+            case CENTER:
+                return "CENTER";
+            case END:
+                return "END";
+            case TOP:
+                return "TOP";
+            case BOTTOM:
+                return "BOTTOM";
+            default:
+                return "NONE";
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index 4d0cbef..47a55b6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -34,6 +34,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 
 import java.util.List;
 
@@ -218,6 +219,8 @@
         boolean checkWeights = true;
         while (checkWeights) {
             checkWeights = false;
+            childrenWidth = 0f;
+            childrenHeight = 0f;
             boolean hasWeights = false;
             float totalWeights = 0f;
             for (Component child : mChildrenComponents) {
@@ -477,4 +480,35 @@
                 mVerticalPositioning,
                 mSpacedBy);
     }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        super.serialize(serializer);
+        serializer.add("verticalPositioning", getPositioningString(mVerticalPositioning));
+        serializer.add("horizontalPositioning", getPositioningString(mHorizontalPositioning));
+        serializer.add("spacedBy", mSpacedBy);
+    }
+
+    private String getPositioningString(int pos) {
+        switch (pos) {
+            case START:
+                return "START";
+            case CENTER:
+                return "CENTER";
+            case END:
+                return "END";
+            case TOP:
+                return "TOP";
+            case BOTTOM:
+                return "BOTTOM";
+            case SPACE_BETWEEN:
+                return "SPACE_BETWEEN";
+            case SPACE_EVENLY:
+                return "SPACE_EVENLY";
+            case SPACE_AROUND:
+                return "SPACE_AROUND";
+            default:
+                return "NONE";
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index 5b35c4c..e93cbd7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -34,6 +34,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 
 import java.util.List;
 
@@ -218,6 +219,8 @@
 
         while (checkWeights) {
             checkWeights = false;
+            childrenWidth = 0f;
+            childrenHeight = 0f;
             boolean hasWeights = false;
             float totalWeights = 0f;
             for (Component child : mChildrenComponents) {
@@ -481,4 +484,35 @@
                 mVerticalPositioning,
                 mSpacedBy);
     }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        super.serialize(serializer);
+        serializer.add("verticalPositioning", getPositioningString(mVerticalPositioning));
+        serializer.add("horizontalPositioning", getPositioningString(mHorizontalPositioning));
+        serializer.add("spacedBy", mSpacedBy);
+    }
+
+    private String getPositioningString(int pos) {
+        switch (pos) {
+            case START:
+                return "START";
+            case CENTER:
+                return "CENTER";
+            case END:
+                return "END";
+            case TOP:
+                return "TOP";
+            case BOTTOM:
+                return "BOTTOM";
+            case SPACE_BETWEEN:
+                return "SPACE_BETWEEN";
+            case SPACE_EVENLY:
+                return "SPACE_EVENLY";
+            case SPACE_AROUND:
+                return "SPACE_AROUND";
+            default:
+                return "NONE";
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
index 3044797..ee16bc2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
@@ -30,6 +30,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -81,6 +82,7 @@
         hideLayoutsOtherThan(currentLayoutIndex);
     }
 
+    /** Traverse the list of children and identify animated components across states */
     public void findAnimatedComponents() {
         for (int i = 0; i < mChildrenComponents.size(); i++) {
             Component cs = mChildrenComponents.get(i);
@@ -105,6 +107,10 @@
         collapsePaintedComponents();
     }
 
+    /**
+     * Traverse the list of components in different states, and if they are similar pick the first
+     * component for painting in all states.
+     */
     public void collapsePaintedComponents() {
         int numStates = mChildrenComponents.size();
         for (Integer id : statePaintedComponents.keySet()) {
@@ -346,6 +352,11 @@
         measuredLayoutIndex = currentLayoutIndex;
     }
 
+    /**
+     * Hides all layouts that are not the one with the given id
+     *
+     * @param idx the layout id
+     */
     public void hideLayoutsOtherThan(int idx) {
         int index = 0;
         for (Component pane : mChildrenComponents) {
@@ -360,6 +371,12 @@
         }
     }
 
+    /**
+     * Returns the layout with the given id
+     *
+     * @param idx the component id
+     * @return the LayoutManager with the given id, or the first child of StateLayout if not found
+     */
     public @NonNull LayoutManager getLayout(int idx) {
         int index = 0;
         for (Component pane : mChildrenComponents) {
@@ -485,6 +502,7 @@
         }
     }
 
+    /** Check if we are at the end of the transition, and if so handles it. */
     public void checkEndOfTransition() {
         LayoutManager currentLayout = getLayout(measuredLayoutIndex);
         LayoutManager previousLayout = getLayout(previousLayoutIndex);
@@ -536,10 +554,16 @@
         return "STATE_LAYOUT";
     }
 
-    //    companion object {
-    //        fun documentation(doc: OrigamiDocumentation) {}
-    //    }
-
+    /**
+     * write the operation to the buffer
+     *
+     * @param buffer the current buffer
+     * @param componentId the component id
+     * @param animationId the animation id if there's one, -1 otherwise.
+     * @param horizontalPositioning the horizontal positioning rule
+     * @param verticalPositioning the vertical positioning rule
+     * @param indexId the current index
+     */
     public static void apply(
             @NonNull WireBuffer buffer,
             int componentId,
@@ -570,4 +594,10 @@
         operations.add(
                 new StateLayout(null, componentId, animationId, 0f, 0f, 100f, 100f, indexId));
     }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        super.serialize(serializer);
+        serializer.add("indexId", mIndexId);
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index 8157ea0..e8e95db 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -34,6 +34,7 @@
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 
 import java.util.List;
 
@@ -331,6 +332,20 @@
         return Operations.LAYOUT_TEXT;
     }
 
+    /**
+     * Write the operation in the buffer
+     *
+     * @param buffer the WireBuffer we write on
+     * @param componentId the component id
+     * @param animationId the animation id (-1 if not set)
+     * @param textId the text id
+     * @param color the text color
+     * @param fontSize the font size
+     * @param fontStyle the font style
+     * @param fontWeight the font weight
+     * @param fontFamilyId the font family id
+     * @param textAlign the alignment rules
+     */
     public static void apply(
             @NonNull WireBuffer buffer,
             int componentId,
@@ -418,4 +433,16 @@
                 mFontFamilyId,
                 mTextAlign);
     }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        super.serialize(serializer);
+        serializer.add("textId", mTextId);
+        serializer.add("color", mColor);
+        serializer.add("fontSize", mFontSize);
+        serializer.add("fontStyle", mFontStyle);
+        serializer.add("fontWeight", mFontWeight);
+        serializer.add("fontFamilyId", mFontFamilyId);
+        serializer.add("textAlign", mTextAlign);
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
index 82f23cd..11ed9f4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
@@ -92,6 +92,11 @@
                 component.mVisibility);
     }
 
+    /**
+     * Initialize this ComponentMeasure from another ComponentMeasure instance.
+     *
+     * @param m the ComponentMeasure to copy from
+     */
     public void copyFrom(@NonNull ComponentMeasure m) {
         mX = m.mX;
         mY = m.mY;
@@ -100,6 +105,12 @@
         mVisibility = m.mVisibility;
     }
 
+    /**
+     * Returns true if the ComponentMeasure passed is identical to us
+     *
+     * @param m the ComponentMeasure to check
+     * @return true if the passed ComponentMeasure is identical to ourself
+     */
     public boolean same(@NonNull ComponentMeasure m) {
         return mX == m.mX && mY == m.mY && mW == m.mW && mH == m.mH && mVisibility == m.mVisibility;
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
index 5cfb1b4..b14f2d9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
@@ -28,10 +28,17 @@
 public class MeasurePass {
     @NonNull HashMap<Integer, ComponentMeasure> mList = new HashMap<>();
 
+    /** Clear the MeasurePass */
     public void clear() {
         mList.clear();
     }
 
+    /**
+     * Add a ComponentMeasure to the MeasurePass
+     *
+     * @param measure the ComponentMeasure to add
+     * @throws Exception
+     */
     public void add(@NonNull ComponentMeasure measure) throws Exception {
         if (measure.mId == -1) {
             throw new Exception("Component has no id!");
@@ -39,10 +46,22 @@
         mList.put(measure.mId, measure);
     }
 
+    /**
+     * Returns true if the current MeasurePass already contains a ComponentMeasure for the given id.
+     *
+     * @param id
+     * @return
+     */
     public boolean contains(int id) {
         return mList.containsKey(id);
     }
 
+    /**
+     * return the ComponentMeasure associated with a given component
+     *
+     * @param c the Component
+     * @return the associated ComponentMeasure
+     */
     public @NonNull ComponentMeasure get(@NonNull Component c) {
         if (!mList.containsKey(c.getComponentId())) {
             ComponentMeasure measure =
@@ -54,6 +73,12 @@
         return mList.get(c.getComponentId());
     }
 
+    /**
+     * Returns the ComponentMeasure associated with the id, creating one if none exists.
+     *
+     * @param id the component id
+     * @return the associated ComponentMeasure
+     */
     public @NonNull ComponentMeasure get(int id) {
         if (!mList.containsKey(id)) {
             ComponentMeasure measure =
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
index b4240d0..ac23db0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
@@ -130,6 +130,20 @@
         return OP_CODE;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer the WireBuffer
+     * @param x x coordinate of the background rect
+     * @param y y coordinate of the background rect
+     * @param width width of the background rect
+     * @param height height of the background rect
+     * @param r red component of the background color
+     * @param g green component of the background color
+     * @param b blue component of the background color
+     * @param a alpha component of the background color
+     * @param shapeType the shape of the background (RECTANGLE=0, CIRCLE=1)
+     */
     public static void apply(
             @NonNull WireBuffer buffer,
             float x,
@@ -205,6 +219,6 @@
                 .field(FLOAT, "g", "")
                 .field(FLOAT, "b", "")
                 .field(FLOAT, "a", "")
-                .field(FLOAT, "shapeType", "");
+                .field(FLOAT, "shapeType", "0 for RECTANGLE, 1 for CIRCLE");
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
index df30d9f..06c21bd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
@@ -176,6 +176,22 @@
         return OP_CODE;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer the WireBuffer
+     * @param x x coordinate of the border rect
+     * @param y y coordinate of the border rect
+     * @param width width of the border rect
+     * @param height height of the border rect
+     * @param borderWidth the width of the border outline
+     * @param roundedCorner rounded corner value in pixels
+     * @param r red component of the border color
+     * @param g green component of the border color
+     * @param b blue component of the border color
+     * @param a alpha component of the border color
+     * @param shapeType the shape type (0 = RECTANGLE, 1 = CIRCLE)
+     */
     public static void apply(
             @NonNull WireBuffer buffer,
             float x,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
index b27fb920..ce44493 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
@@ -76,6 +76,11 @@
         return OP_CODE;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer the WireBuffer
+     */
     public static void apply(@NonNull WireBuffer buffer) {
         buffer.start(OP_CODE);
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
index a1609ac..dd27f8b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
@@ -21,6 +21,7 @@
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.PaintOperation;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.SerializableToString;
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
@@ -36,7 +37,7 @@
 
 /** Maintain a list of modifiers */
 public class ComponentModifiers extends PaintOperation
-        implements DecoratorComponent, ClickHandler, TouchHandler {
+        implements DecoratorComponent, ClickHandler, TouchHandler, SerializableToString {
     @NonNull ArrayList<ModifierOperation> mList = new ArrayList<>();
 
     @NonNull
@@ -68,6 +69,7 @@
         // nothing
     }
 
+    @Override
     public void serializeToString(int indent, @NonNull StringSerializer serializer) {
         serializer.append(indent, "MODIFIERS");
         for (ModifierOperation m : mList) {
@@ -75,10 +77,20 @@
         }
     }
 
+    /**
+     * Add a ModifierOperation
+     *
+     * @param operation a ModifierOperation
+     */
     public void add(@NonNull ModifierOperation operation) {
         mList.add(operation);
     }
 
+    /**
+     * Returns the size of the modifier list
+     *
+     * @return number of modifiers
+     */
     public int size() {
         return mList.size();
     }
@@ -133,6 +145,11 @@
         }
     }
 
+    /**
+     * Add the operations to this ComponentModifier
+     *
+     * @param operations list of ModifierOperation
+     */
     public void addAll(@NonNull ArrayList<ModifierOperation> operations) {
         mList.addAll(operations);
     }
@@ -197,6 +214,11 @@
         }
     }
 
+    /**
+     * Returns true if we have a horizontal scroll modifier
+     *
+     * @return true if we have a horizontal scroll modifier, false otherwise
+     */
     public boolean hasHorizontalScroll() {
         for (ModifierOperation op : mList) {
             if (op instanceof ScrollModifierOperation) {
@@ -209,6 +231,11 @@
         return false;
     }
 
+    /**
+     * Returns true if we have a vertical scroll modifier
+     *
+     * @return true if we have a vertical scroll modifier, false otherwise
+     */
     public boolean hasVerticalScroll() {
         for (ModifierOperation op : mList) {
             if (op instanceof ScrollModifierOperation) {
@@ -221,6 +248,12 @@
         return false;
     }
 
+    /**
+     * Set the horizontal scroll dimension (if we have a scroll modifier)
+     *
+     * @param hostDimension the host component horizontal dimension
+     * @param contentDimension the content horizontal dimension
+     */
     public void setHorizontalScrollDimension(float hostDimension, float contentDimension) {
         for (ModifierOperation op : mList) {
             if (op instanceof ScrollModifierOperation) {
@@ -232,6 +265,12 @@
         }
     }
 
+    /**
+     * Set the vertical scroll dimension (if we have a scroll modifier)
+     *
+     * @param hostDimension the host component vertical dimension
+     * @param contentDimension the content vertical dimension
+     */
     public void setVerticalScrollDimension(float hostDimension, float contentDimension) {
         for (ModifierOperation op : mList) {
             if (op instanceof ScrollModifierOperation) {
@@ -243,6 +282,11 @@
         }
     }
 
+    /**
+     * Returns the horizontal scroll dimension if we have a scroll modifier
+     *
+     * @return the horizontal scroll dimension, or 0 if no scroll modifier
+     */
     public float getHorizontalScrollDimension() {
         for (ModifierOperation op : mList) {
             if (op instanceof ScrollModifierOperation) {
@@ -255,6 +299,11 @@
         return 0f;
     }
 
+    /**
+     * Returns the vertical scroll dimension if we have a scroll modifier
+     *
+     * @return the vertical scroll dimension, or 0 if no scroll modifier
+     */
     public float getVerticalScrollDimension() {
         for (ModifierOperation op : mList) {
             if (op instanceof ScrollModifierOperation) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
index c377b75..dd22391 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
@@ -52,6 +52,11 @@
         return "ComponentVisibilityOperation(" + mVisibilityId + ")";
     }
 
+    /**
+     * Returns the serialized name for this operation
+     *
+     * @return the serialized name
+     */
     @NonNull
     public String serializedName() {
         return "COMPONENT_VISIBILITY";
@@ -74,6 +79,12 @@
     @Override
     public void write(@NonNull WireBuffer buffer) {}
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param valueId visibility value
+     */
     public static void apply(@NonNull WireBuffer buffer, int valueId) {
         buffer.start(OP_CODE);
         buffer.writeInt(valueId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionInModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionInModifierOperation.java
new file mode 100644
index 0000000..7c9acfe
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionInModifierOperation.java
@@ -0,0 +1,109 @@
+/*
+ * 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.widget.remotecompose.core.operations.layout.modifiers;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+/** Helper class to set the min / max dimension on a component */
+public class DimensionInModifierOperation extends Operation
+        implements ModifierOperation, VariableSupport {
+    int mOpCode = -1;
+
+    float mV1;
+    float mV2;
+    float mValue1;
+    float mValue2;
+
+    public DimensionInModifierOperation(int opcode, float min, float max) {
+        mOpCode = opcode;
+        mValue1 = min;
+        mValue2 = max;
+        if (!Float.isNaN(mValue1)) {
+            mV1 = mValue1;
+        }
+        if (!Float.isNaN(mValue2)) {
+            mV2 = mValue2;
+        }
+    }
+
+    @Override
+    public void updateVariables(@NonNull RemoteContext context) {
+        mV1 = Float.isNaN(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+        mV2 = Float.isNaN(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+        if (mV1 != -1) {
+            mV1 = mV1 * context.getDensity();
+        }
+        if (mV2 != -1) {
+            mV2 = mV2 * context.getDensity();
+        }
+    }
+
+    @Override
+    public void registerListening(@NonNull RemoteContext context) {
+        if (Float.isNaN(mValue1)) {
+            context.listensTo(Utils.idFromNan(mValue1), this);
+        }
+        if (Float.isNaN(mValue2)) {
+            context.listensTo(Utils.idFromNan(mValue2), this);
+        }
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        // nothing
+    }
+
+    @Override
+    public void apply(@NonNull RemoteContext context) {
+        // nothing
+    }
+
+    @NonNull
+    @Override
+    public String deepToString(@NonNull String indent) {
+        return indent + toString();
+    }
+
+    /**
+     * Returns the min value
+     *
+     * @return minimum value
+     */
+    public float getMin() {
+        return mV1;
+    }
+
+    /**
+     * Returns the max value
+     *
+     * @return maximum value
+     */
+    public float getMax() {
+        return mV2;
+    }
+
+    @Override
+    public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+        serializer.append(indent, "WIDTH_IN = [" + getMin() + ", " + getMax() + "]");
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
index b11deae..88449c4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
@@ -104,6 +104,11 @@
         }
     }
 
+    /**
+     * Returns true if the dimension is set using a weight
+     *
+     * @return true if using weight, false otherwise
+     */
     public boolean hasWeight() {
         return mType == Type.WEIGHT;
     }
@@ -136,6 +141,11 @@
         mOutValue = mValue = value;
     }
 
+    /**
+     * Returns the serialized name for this operation
+     *
+     * @return the serialized name
+     */
     @NonNull
     public String serializedName() {
         return "DIMENSION";
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
index 15c2f46..dc59180 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
@@ -222,6 +222,26 @@
         return OP_CODE;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param scaleX scaleX of the layer
+     * @param scaleY scaleY of the layer
+     * @param rotationX rotationX of the layer
+     * @param rotationY rotationY of the layer
+     * @param rotationZ rotationZ of the layer
+     * @param shadowElevation the shadow elevation
+     * @param transformOriginX the X origin of the transformations
+     * @param transformOriginY the Y origin of the transformations
+     * @param alpha the alpha of the layer
+     * @param cameraDistance the camera distance
+     * @param blendMode blending mode of the layer
+     * @param spotShadowColorId the spot shadow color id
+     * @param ambientShadowColorId the ambient shadow color id
+     * @param colorFilterId the color filter id
+     * @param renderEffectId the render effect id
+     */
     public static void apply(
             WireBuffer buffer,
             float scaleX,
@@ -257,6 +277,12 @@
         buffer.writeInt(renderEffectId);
     }
 
+    /**
+     * Read the operation from the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param operations the list of operations read so far
+     */
     public static void read(WireBuffer buffer, List<Operation> operations) {
         float scaleX = buffer.readFloat();
         float scaleY = buffer.readFloat();
@@ -292,6 +318,11 @@
                         renderEffectId));
     }
 
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
                 .description("define the GraphicsLayer Modifier")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java
index c19bd2f..cc32f26 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java
@@ -19,20 +19,27 @@
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
-import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
-import com.android.internal.widget.remotecompose.core.operations.DrawBase2;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 
 import java.util.List;
 
 /** Set the min / max height dimension on a component */
-public class HeightInModifierOperation extends DrawBase2 implements ModifierOperation {
+public class HeightInModifierOperation extends DimensionInModifierOperation {
     private static final int OP_CODE = Operations.MODIFIER_HEIGHT_IN;
     public static final String CLASS_NAME = "HeightInModifierOperation";
 
+    public HeightInModifierOperation(float min, float max) {
+        super(OP_CODE, min, max);
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, getMin(), getMax());
+    }
+
     /**
      * Read this operation and add it to the list of operations
      *
@@ -40,26 +47,9 @@
      * @param operations the list of operations that will be added to
      */
     public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
-        Maker m = HeightInModifierOperation::new;
-        read(m, buffer, operations);
-    }
-
-    /**
-     * Returns the min value
-     *
-     * @return minimum value
-     */
-    public float getMin() {
-        return mV1;
-    }
-
-    /**
-     * Returns the max value
-     *
-     * @return maximum value
-     */
-    public float getMax() {
-        return mV2;
+        float v1 = buffer.readFloat();
+        float v2 = buffer.readFloat();
+        operations.add(new HeightInModifierOperation(v1, v2));
     }
 
     /**
@@ -81,11 +71,6 @@
         return CLASS_NAME;
     }
 
-    @Override
-    protected void write(@NonNull WireBuffer buffer, float v1, float v2) {
-        apply(buffer, v1, v2);
-    }
-
     /**
      * Populate the documentation with a description of this operation
      *
@@ -98,14 +83,6 @@
                 .field(DocumentedOperation.FLOAT, "max", "The maximum height, -1 if not applied");
     }
 
-    public HeightInModifierOperation(float min, float max) {
-        super(min, max);
-        mName = CLASS_NAME;
-    }
-
-    @Override
-    public void paint(@NonNull PaintContext context) {}
-
     /**
      * Writes out the HeightInModifier to the buffer
      *
@@ -114,7 +91,9 @@
      * @param y1 start y of the DrawOval
      */
     public static void apply(@NonNull WireBuffer buffer, float x1, float y1) {
-        write(buffer, OP_CODE, x1, y1);
+        buffer.start(OP_CODE);
+        buffer.writeFloat(x1);
+        buffer.writeFloat(y1);
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
index 4b50a91..154740d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
@@ -52,6 +52,13 @@
         return OP_CODE;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param type the type of dimension rule (DimensionModifierOperation.Type)
+     * @param value the value of the dimension
+     */
     public static void apply(@NonNull WireBuffer buffer, int type, float value) {
         buffer.start(OP_CODE);
         buffer.writeInt(type);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
index 2e9d661..09e2228 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
@@ -23,6 +23,7 @@
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.SerializableToString;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
 import com.android.internal.widget.remotecompose.core.operations.layout.ActionOperation;
@@ -32,7 +33,8 @@
 import java.util.List;
 
 /** Capture a host action information. This can be triggered on eg. a click. */
-public class HostActionOperation extends Operation implements ActionOperation {
+public class HostActionOperation extends Operation
+        implements ActionOperation, SerializableToString {
     private static final int OP_CODE = Operations.HOST_ACTION;
 
     int mActionId = -1;
@@ -51,6 +53,11 @@
         return mActionId;
     }
 
+    /**
+     * Returns the serialized name for this operation
+     *
+     * @return the serialized name
+     */
     @NonNull
     public String serializedName() {
         return "HOST_ACTION";
@@ -83,6 +90,12 @@
         context.runAction(mActionId, "");
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param actionId the action id
+     */
     public static void apply(@NonNull WireBuffer buffer, int actionId) {
         buffer.start(OP_CODE);
         buffer.writeInt(actionId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
index 49ef58e..8a8809c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
@@ -57,6 +57,11 @@
         return "HostNamedActionOperation(" + mTextId + " : " + mValueId + ")";
     }
 
+    /**
+     * Name used during serialization
+     *
+     * @return the serialized name for this operation
+     */
     @NonNull
     public String serializedName() {
         return "HOST_NAMED_ACTION";
@@ -105,6 +110,14 @@
         context.runNamedAction(mTextId, value);
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param textId the text id of the action
+     * @param type the type of the action
+     * @param valueId the value id associated with the action
+     */
     public static void apply(@NonNull WireBuffer buffer, int textId, int type, int valueId) {
         buffer.start(OP_CODE);
         buffer.writeInt(textId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java
index 9588e99..4ad11d2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java
@@ -110,6 +110,12 @@
                 mVelocity);
     }
 
+    /**
+     * Serialize the string
+     *
+     * @param indent padding to display
+     * @param serializer append the string
+     */
     // @Override
     public void serializeToString(int indent, StringSerializer serializer) {
         serializer.append(indent, "MARQUEE = [" + mIterations + "]");
@@ -153,14 +159,35 @@
         return "MarqueeModifierOperation(" + mIterations + ")";
     }
 
+    /**
+     * Name of the operation
+     *
+     * @return name
+     */
     public static String name() {
         return CLASS_NAME;
     }
 
+    /**
+     * id of the operation
+     *
+     * @return the operation id
+     */
     public static int id() {
         return OP_CODE;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param iterations the number of iterations
+     * @param animationMode animation mode
+     * @param repeatDelayMillis repeat delay in ms
+     * @param initialDelayMillis initial delay before the marquee start in ms
+     * @param spacing the spacing between marquee
+     * @param velocity the velocity of the marquee animation
+     */
     public static void apply(
             WireBuffer buffer,
             int iterations,
@@ -178,6 +205,12 @@
         buffer.writeFloat(velocity);
     }
 
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int iterations = buffer.readInt();
         int animationMode = buffer.readInt();
@@ -195,6 +228,11 @@
                         velocity));
     }
 
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
                 .description("specify a Marquee Modifier")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
index f8926fe..a86fb2c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
@@ -22,5 +22,11 @@
 
 /** Represents a modifier */
 public interface ModifierOperation extends OperationInterface {
+    /**
+     * Serialize the string
+     *
+     * @param indent padding to display
+     * @param serializer append the string
+     */
     void serializeToString(int indent, @NonNull StringSerializer serializer);
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
index 4271947..2cd2728 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
@@ -65,6 +65,12 @@
         apply(buffer, mX, mY);
     }
 
+    /**
+     * Serialize the string
+     *
+     * @param indent padding to display
+     * @param serializer append the string
+     */
     // @Override
     public void serializeToString(int indent, StringSerializer serializer) {
         serializer.append(indent, "OFFSET = [" + mX + ", " + mY + "]");
@@ -110,18 +116,36 @@
         return OP_CODE;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param x x offset
+     * @param y y offset
+     */
     public static void apply(WireBuffer buffer, float x, float y) {
         buffer.start(OP_CODE);
         buffer.writeFloat(x);
         buffer.writeFloat(y);
     }
 
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
     public static void read(WireBuffer buffer, List<Operation> operations) {
         float x = buffer.readFloat();
         float y = buffer.readFloat();
         operations.add(new OffsetModifierOperation(x, y));
     }
 
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
                 .description("define the Offset Modifier")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
index bcfbdd6..3225d5c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
@@ -132,6 +132,15 @@
         return Operations.MODIFIER_PADDING;
     }
 
+    /**
+     * Write operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param left left padding
+     * @param top top padding
+     * @param right right padding
+     * @param bottom bottom padding
+     */
     public static void apply(
             @NonNull WireBuffer buffer, float left, float top, float right, float bottom) {
         buffer.start(Operations.MODIFIER_PADDING);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java
index fe074e4..9787d9b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java
@@ -143,19 +143,40 @@
         serializer.append(indent, "RIPPLE_MODIFIER");
     }
 
+    /**
+     * The operation name
+     *
+     * @return operation name
+     */
     @NonNull
     public static String name() {
         return "RippleModifier";
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     */
     public static void apply(@NonNull WireBuffer buffer) {
         buffer.start(OP_CODE);
     }
 
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
     public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
         operations.add(new RippleModifierOperation());
     }
 
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
     public static void documentation(@NonNull DocumentationBuilder doc) {
         doc.operation("Layout Operations", OP_CODE, name())
                 .description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
index 8950579..76b3373 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
@@ -135,6 +135,12 @@
         apply(buffer, mDirection, mPositionExpression, mMax, mNotchMax);
     }
 
+    /**
+     * Serialize the string
+     *
+     * @param indent padding to display
+     * @param serializer append the string
+     */
     // @Override
     public void serializeToString(int indent, StringSerializer serializer) {
         serializer.append(indent, "SCROLL = [" + mDirection + "]");
@@ -190,6 +196,15 @@
         return OP_CODE;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param direction direction of the scroll (HORIZONTAL, VERTICAL)
+     * @param position the current position
+     * @param max the maximum position
+     * @param notchMax the maximum notch
+     */
     public static void apply(
             WireBuffer buffer, int direction, float position, float max, float notchMax) {
         buffer.start(OP_CODE);
@@ -199,6 +214,12 @@
         buffer.writeFloat(notchMax);
     }
 
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int direction = buffer.readInt();
         float position = buffer.readFloat();
@@ -207,6 +228,11 @@
         operations.add(new ScrollModifierOperation(direction, position, max, notchMax));
     }
 
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
                 .description("define a Scroll Modifier")
@@ -300,12 +326,24 @@
     public void onTouchCancel(
             RemoteContext context, CoreDocument document, Component component, float x, float y) {}
 
+    /**
+     * Set the horizontal scroll dimension
+     *
+     * @param hostDimension the horizontal host dimension
+     * @param contentDimension the horizontal content dimension
+     */
     public void setHorizontalScrollDimension(float hostDimension, float contentDimension) {
         mHostDimension = hostDimension;
         mContentDimension = contentDimension;
         mMaxScrollX = contentDimension - hostDimension;
     }
 
+    /**
+     * Set the vertical scroll dimension
+     *
+     * @param hostDimension the vertical host dimension
+     * @param contentDimension the vertical content dimension
+     */
     public void setVerticalScrollDimension(float hostDimension, float contentDimension) {
         mHostDimension = hostDimension;
         mContentDimension = contentDimension;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
index b6977a0..d625900 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
@@ -49,6 +49,11 @@
         return "ValueFloatChangeActionOperation(" + mTargetValueId + ")";
     }
 
+    /**
+     * The name of the operation used during serialization
+     *
+     * @return the operation serialized name
+     */
     public String serializedName() {
         return "VALUE_FLOAT_CHANGE";
     }
@@ -76,18 +81,36 @@
         context.overrideFloat(mTargetValueId, mValue);
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param valueId the value id
+     * @param value the value to set
+     */
     public static void apply(WireBuffer buffer, int valueId, float value) {
         buffer.start(OP_CODE);
         buffer.writeInt(valueId);
         buffer.writeFloat(value);
     }
 
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int valueId = buffer.readInt();
         float value = buffer.readFloat();
         operations.add(new ValueFloatChangeActionOperation(valueId, value));
     }
 
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", OP_CODE, "ValueFloatChangeActionOperation")
                 .description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java
index 766271a..3f26c5e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java
@@ -50,6 +50,11 @@
         return "ValueFloatExpressionChangeActionOperation(" + mTargetValueId + ")";
     }
 
+    /**
+     * The name of the operation used during serialization
+     *
+     * @return the operation serialized name
+     */
     @NonNull
     public String serializedName() {
         return "VALUE_FLOAT_EXPRESSION_CHANGE";
@@ -83,6 +88,13 @@
         document.evaluateFloatExpression(mValueExpressionId, mTargetValueId, context);
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param valueId the value id
+     * @param value the value to set
+     */
     public static void apply(@NonNull WireBuffer buffer, int valueId, int value) {
         buffer.start(OP_CODE);
         buffer.writeInt(valueId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
index 60166a7..8c5bb6f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
@@ -49,6 +49,11 @@
         return "ValueChangeActionOperation(" + mTargetValueId + ")";
     }
 
+    /**
+     * The name of the operation used during serialization
+     *
+     * @return the operation serialized name
+     */
     @NonNull
     public String serializedName() {
         return "VALUE_INTEGER_CHANGE";
@@ -81,6 +86,13 @@
         context.overrideInteger(mTargetValueId, mValue);
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param valueId the value id
+     * @param value the value to set
+     */
     public static void apply(@NonNull WireBuffer buffer, int valueId, int value) {
         buffer.start(OP_CODE);
         buffer.writeInt(valueId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
index 5025080..00c80f1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
@@ -50,6 +50,11 @@
         return "ValueIntegerExpressionChangeActionOperation(" + mTargetValueId + ")";
     }
 
+    /**
+     * The name of the operation used during serialization
+     *
+     * @return the operation serialized name
+     */
     @NonNull
     public String serializedName() {
         return "VALUE_INTEGER_EXPRESSION_CHANGE";
@@ -83,6 +88,13 @@
         document.evaluateIntExpression(mValueExpressionId, (int) mTargetValueId, context);
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param valueId the long id pointing to an int value
+     * @param value the value to set (long id)`
+     */
     public static void apply(@NonNull WireBuffer buffer, long valueId, long value) {
         buffer.start(OP_CODE);
         buffer.writeLong(valueId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
index 8093bb3..57e30d4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
@@ -53,6 +53,11 @@
         return mTargetValueId;
     }
 
+    /**
+     * The name of the operation used during serialization
+     *
+     * @return the operation serialized name
+     */
     @NonNull
     public String serializedName() {
         return "VALUE_CHANGE";
@@ -85,6 +90,13 @@
         context.overrideText(mTargetValueId, mValueId);
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param valueId the string id
+     * @param value the value to set (string id)`
+     */
     public static void apply(@NonNull WireBuffer buffer, int valueId, int value) {
         buffer.start(OP_CODE);
         buffer.writeInt(valueId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java
index c3624e5..8c1ffbd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java
@@ -19,20 +19,27 @@
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
-import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
-import com.android.internal.widget.remotecompose.core.operations.DrawBase2;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 
 import java.util.List;
 
 /** Set the min / max width dimension on a component */
-public class WidthInModifierOperation extends DrawBase2 implements ModifierOperation {
+public class WidthInModifierOperation extends DimensionInModifierOperation {
     private static final int OP_CODE = Operations.MODIFIER_WIDTH_IN;
     public static final String CLASS_NAME = "WidthInModifierOperation";
 
+    public WidthInModifierOperation(float min, float max) {
+        super(OP_CODE, min, max);
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, getMin(), getMax());
+    }
+
     /**
      * Read this operation and add it to the list of operations
      *
@@ -40,26 +47,9 @@
      * @param operations the list of operations that will be added to
      */
     public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
-        Maker m = WidthInModifierOperation::new;
-        read(m, buffer, operations);
-    }
-
-    /**
-     * Returns the min value
-     *
-     * @return minimum value
-     */
-    public float getMin() {
-        return mV1;
-    }
-
-    /**
-     * Returns the max value
-     *
-     * @return maximum value
-     */
-    public float getMax() {
-        return mV2;
+        float v1 = buffer.readFloat();
+        float v2 = buffer.readFloat();
+        operations.add(new WidthInModifierOperation(v1, v2));
     }
 
     /**
@@ -81,11 +71,6 @@
         return CLASS_NAME;
     }
 
-    @Override
-    protected void write(@NonNull WireBuffer buffer, float v1, float v2) {
-        apply(buffer, v1, v2);
-    }
-
     /**
      * Populate the documentation with a description of this operation
      *
@@ -98,14 +83,6 @@
                 .field(DocumentedOperation.FLOAT, "max", "The maximum width, -1 if not applied");
     }
 
-    public WidthInModifierOperation(float min, float max) {
-        super(min, max);
-        mName = CLASS_NAME;
-    }
-
-    @Override
-    public void paint(@NonNull PaintContext context) {}
-
     /**
      * Writes out the WidthInModifier to the buffer
      *
@@ -114,7 +91,9 @@
      * @param y1 start y of the DrawOval
      */
     public static void apply(@NonNull WireBuffer buffer, float x1, float y1) {
-        write(buffer, OP_CODE, x1, y1);
+        buffer.start(OP_CODE);
+        buffer.writeFloat(x1);
+        buffer.writeFloat(y1);
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
index 532027a..687238e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
@@ -52,6 +52,13 @@
         return OP_CODE;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param type the type of dimension rule (DimensionModifierOperation.Type)
+     * @param value the value of the dimension
+     */
     public static void apply(@NonNull WireBuffer buffer, int type, float value) {
         buffer.start(OP_CODE);
         buffer.writeInt(type);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
index 35de33a..52841a7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
@@ -55,6 +55,12 @@
         apply(buffer, mValue);
     }
 
+    /**
+     * Serialize the string
+     *
+     * @param indent padding to display
+     * @param serializer append the string
+     */
     // @Override
     public void serializeToString(int indent, StringSerializer serializer) {
         serializer.append(indent, "ZINDEX = [" + mValue + "]");
@@ -99,16 +105,33 @@
         return OP_CODE;
     }
 
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param value the z-index value
+     */
     public static void apply(WireBuffer buffer, float value) {
         buffer.start(OP_CODE);
         buffer.writeFloat(value);
     }
 
+    /**
+     * Read this operation and add it to the list of operations
+     *
+     * @param buffer the buffer to read
+     * @param operations the list of operations that will be added to
+     */
     public static void read(WireBuffer buffer, List<Operation> operations) {
         float value = buffer.readFloat();
         operations.add(new ZIndexModifierOperation(value));
     }
 
+    /**
+     * Populate the documentation with a description of this operation
+     *
+     * @param doc to append the description to.
+     */
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
                 .description("define the Z-Index Modifier")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
index 9543469..4c7f503e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
@@ -682,9 +682,9 @@
      * @param radius Must be positive. The radius of the gradient.
      * @param colors The sRGB colors distributed between the center and edge
      * @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and <code>
-     *      1.0f</code>. The relative position of each corresponding color in the colors array. If
-     *     <code>null</code>, colors are distributed evenly between the center and edge of the
-     *     circle.
+     *                 1.0f</code>. The relative position of each corresponding color in the colors
+     *     array. If <code>null</code>, colors are distributed evenly between the center and edge of
+     *     the circle.
      * @param tileMode The Shader tiling mode
      */
     public void setRadialGradient(
@@ -808,7 +808,7 @@
     }
 
     /**
-     * Set the color based the R,G,B,A values
+     * Set the color based the R,G,B,A values (Warning this does not support NaN ids)
      *
      * @param r red (0.0 to 1.0)
      * @param g green (0.0 to 1.0)
@@ -816,7 +816,7 @@
      * @param a alpha (0.0 to 1.0)
      */
     public void setColor(float r, float g, float b, float a) {
-        setColor((int) (r * 255), (int) (g * 255), (int) (b * 255), (int) (a * 255));
+        setColor(Utils.toARGB(a, r, g, b));
     }
 
     /**
@@ -897,6 +897,11 @@
         mPos++;
     }
 
+    /**
+     * set Filter Bitmap
+     *
+     * @param filter set to false to disable interpolation
+     */
     public void setFilterBitmap(boolean filter) {
         mArray[mPos] = FILTER_BITMAP | (filter ? (1 << 16) : 0);
         mPos++;
@@ -944,6 +949,12 @@
         }
     }
 
+    /**
+     * Convert a blend mode integer as a string
+     *
+     * @param mode the blend mode
+     * @return the blend mode as a string
+     */
     public static @NonNull String blendModeString(int mode) {
         switch (mode) {
             case PaintBundle.BLEND_MODE_CLEAR:
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/TextPaint.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/TextPaint.java
index ff6f45d..2812eed 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/TextPaint.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/TextPaint.java
@@ -17,51 +17,250 @@
 
 import android.annotation.NonNull;
 
+import java.util.Locale;
+
 // TODO: this interface is unused. Delete it.
 public interface TextPaint {
+
+    /**
+     * Helper to setColor(), that takes a,r,g,b and constructs the color int
+     *
+     * @param a The new alpha component (0..255) of the paint's color.
+     * @param r The new red component (0..255) of the paint's color.
+     * @param g The new green component (0..255) of the paint's color.
+     * @param b The new blue component (0..255) of the paint's color.
+     */
     void setARGB(int a, int r, int g, int b);
 
+    /**
+     * Helper for setFlags(), setting or clearing the DITHER_FLAG bit Dithering affects how colors
+     * that are higher precision than the device are down-sampled. No dithering is generally faster,
+     * but higher precision colors are just truncated down (e.g. 8888 -> 565). Dithering tries to
+     * distribute the error inherent in this process, to reduce the visual artifacts.
+     *
+     * @param dither true to set the dithering bit in flags, false to clear it
+     */
     void setDither(boolean dither);
 
+    /**
+     * Set the paint's elegant height metrics flag. This setting selects font variants that have not
+     * been compacted to fit Latin-based vertical metrics, and also increases top and bottom bounds
+     * to provide more space.
+     *
+     * @param elegant set the paint's elegant metrics flag for drawing text.
+     */
     void setElegantTextHeight(boolean elegant);
 
+    /**
+     * Set a end hyphen edit on the paint.
+     *
+     * <p>By setting end hyphen edit, the measurement and drawing is performed with modifying
+     * hyphenation at the end of line. For example, by passing character is appended at the end of
+     * line.
+     *
+     * <pre>
+     * <code>
+     *   Paint paint = new Paint();
+     *   paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN);
+     *   paint.measureText("abc", 0, 3);  // Returns the width of "abc-"
+     *   Canvas.drawText("abc", 0, 3, 0f, 0f, paint);  // Draws "abc-"
+     * </code>
+     * </pre>
+     *
+     * @param endHyphen a end hyphen edit value.
+     */
     void setEndHyphenEdit(int endHyphen);
 
+    /**
+     * Helper for setFlags(), setting or clearing the FAKE_BOLD_TEXT_FLAG bit
+     *
+     * @param fakeBoldText true to set the fakeBoldText bit in the paint's flags, false to clear it.
+     */
     void setFakeBoldText(boolean fakeBoldText);
 
+    /**
+     * Set the paint's flags. Use the Flag enum to specific flag values.
+     *
+     * @param flags The new flag bits for the paint
+     */
     void setFlags(int flags);
 
+    /**
+     * Set font feature settings.
+     *
+     * <p>The format is the same as the CSS font-feature-settings attribute: <a
+     * href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
+     * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop</a>
+     *
+     * @param settings the font feature settings string to use, may be null.
+     */
     void setFontFeatureSettings(@NonNull String settings);
 
+    /**
+     * Set the paint's hinting mode. May be either
+     *
+     * @param mode The new hinting mode. (HINTING_OFF or HINTING_ON)
+     */
     void setHinting(int mode);
 
+    /**
+     * Set the paint's letter-spacing for text. The default value is 0. The value is in 'EM' units.
+     * Typical values for slight expansion will be around 0.05. Negative values tighten text.
+     *
+     * @param letterSpacing set the paint's letter-spacing for drawing text.
+     */
     void setLetterSpacing(float letterSpacing);
 
+    /**
+     * Helper for setFlags(), setting or clearing the LINEAR_TEXT_FLAG bit
+     *
+     * @param linearText true to set the linearText bit in the paint's flags, false to clear it.
+     */
     void setLinearText(boolean linearText);
 
+    /**
+     * This draws a shadow layer below the main layer, with the specified offset and color, and blur
+     * radius. If radius is 0, then the shadow layer is removed.
+     *
+     * <p>Can be used to create a blurred shadow underneath text. Support for use with other drawing
+     * operations is constrained to the software rendering pipeline.
+     *
+     * <p>The alpha of the shadow will be the paint's alpha if the shadow color is opaque, or the
+     * alpha from the shadow color if not.
+     *
+     * @param radius the radius of the shadows
+     * @param dx the x offset of the shadow
+     * @param dy the y offset of the shadow
+     * @param shadowColor the color of the shadow
+     */
     void setShadowLayer(float radius, float dx, float dy, int shadowColor);
 
+    /**
+     * Set a start hyphen edit on the paint.
+     *
+     * <p>By setting start hyphen edit, the measurement and drawing is performed with modifying
+     * hyphenation at the start of line. For example, by passing character is appended at the start
+     * of line.
+     *
+     * <pre>
+     * <code>
+     *   Paint paint = new Paint();
+     *   paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_INSERT_HYPHEN);
+     *   paint.measureText("abc", 0, 3);  // Returns the width of "-abc"
+     *   Canvas.drawText("abc", 0, 3, 0f, 0f, paint);  // Draws "-abc"
+     * </code>
+     * </pre>
+     *
+     * The default value is 0 which is equivalent to
+     *
+     * @param startHyphen a start hyphen edit value.
+     */
     void setStartHyphenEdit(int startHyphen);
 
+    /**
+     * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit
+     *
+     * @param strikeThruText true to set the strikeThruText bit in the paint's flags, false to clear
+     *     it.
+     */
     void setStrikeThruText(boolean strikeThruText);
 
+    /**
+     * Set the paint's Cap.
+     *
+     * @param cap set the paint's line cap style, used whenever the paint's style is Stroke or
+     *     StrokeAndFill.
+     */
     void setStrokeCap(int cap);
 
+    /**
+     * Helper for setFlags(), setting or clearing the SUBPIXEL_TEXT_FLAG bit
+     *
+     * @param subpixelText true to set the subpixelText bit in the paint's flags, false to clear it.
+     */
     void setSubpixelText(boolean subpixelText);
 
+    /**
+     * Set the paint's text alignment. This controls how the text is positioned relative to its
+     * origin. LEFT align means that all of the text will be drawn to the right of its origin (i.e.
+     * the origin specifies the LEFT edge of the text) and so on.
+     *
+     * @param align set the paint's Align value for drawing text.
+     */
     void setTextAlign(int align);
 
+    /**
+     * Set the text locale list to a one-member list consisting of just the locale.
+     *
+     * @param locale the paint's locale value for drawing text, must not be null.
+     */
     void setTextLocale(int locale);
 
+    /**
+     * Set the text locale list.
+     *
+     * <p>The text locale list affects how the text is drawn for some languages.
+     *
+     * <p>For example, if the locale list contains {@link Locale#CHINESE} or {@link Locale#CHINA},
+     * then the text renderer will prefer to draw text using a Chinese font. Likewise, if the locale
+     * list contains {@link Locale#JAPANESE} or {@link Locale#JAPAN}, then the text renderer will
+     * prefer to draw text using a Japanese font. If the locale list contains both, the order those
+     * locales appear in the list is considered for deciding the font.
+     *
+     * <p>This distinction is important because Chinese and Japanese text both use many of the same
+     * Unicode code points but their appearance is subtly different for each language.
+     *
+     * <p>By default, the text locale list is initialized to a one-member list just containing the
+     * system locales. This assumes that the text to be rendered will most likely be in the user's
+     * preferred language.
+     *
+     * <p>If the actual language or languages of the text is/are known, then they can be provided to
+     * the text renderer using this method. The text renderer may attempt to guess the language
+     * script based on the contents of the text to be drawn independent of the text locale here.
+     * Specifying the text locales just helps it do a better job in certain ambiguous cases.
+     *
+     * @param localesArray the paint's locale list for drawing text, must not be null or empty.
+     */
     void setTextLocales(int localesArray);
 
+    /**
+     * Set the paint's horizontal scale factor for text. The default value is 1.0. Values > 1.0 will
+     * stretch the text wider. Values < 1.0 will stretch the text narrower.
+     *
+     * @param scaleX set the paint's scale in X for drawing/measuring text.
+     */
     void setTextScaleX(float scaleX);
 
+    /**
+     * Set the paint's text size. This value must be > 0
+     *
+     * @param textSize set the paint's text size in pixel units.
+     */
     void setTextSize(float textSize);
 
+    /**
+     * Set the paint's horizontal skew factor for text. The default value is 0. For approximating
+     * oblique text, use values around -0.25.
+     *
+     * @param skewX set the paint's skew factor in X for drawing text.
+     */
     void setTextSkewX(float skewX);
 
+    /**
+     * Helper for setFlags(), setting or clearing the UNDERLINE_TEXT_FLAG bit
+     *
+     * @param underlineText true to set the underlineText bit in the paint's flags, false to clear
+     *     it.
+     */
     void setUnderlineText(boolean underlineText);
 
+    /**
+     * Set the paint's extra word-spacing for text.
+     *
+     * <p>Increases the white space width between words with the given amount of pixels. The default
+     * value is 0.
+     *
+     * @param wordSpacing set the paint's extra word-spacing for drawing text in pixels.
+     */
     void setWordSpacing(float wordSpacing);
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
index b92f96f..704f6ce 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
@@ -22,6 +22,14 @@
  * unavailable
  */
 public interface CollectionsAccess {
+
+    /**
+     * Get the float value in the array at the given index
+     *
+     * @param id the id of the float array
+     * @param index the index of the value
+     * @return
+     */
     float getFloatValue(int id, int index);
 
     /**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
index 07a3d84..04beba3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
@@ -28,6 +28,12 @@
         mIds = ids;
     }
 
+    /**
+     * Return position for given string
+     *
+     * @param str string
+     * @return position associated with the string
+     */
     public int getPos(@NonNull String str) {
         for (int i = 0; i < mNames.length; i++) {
             String name = mNames[i];
@@ -38,10 +44,22 @@
         return -1;
     }
 
+    /**
+     * Return type for given index
+     *
+     * @param pos index
+     * @return type at index
+     */
     public byte getType(int pos) {
         return mTypes[pos];
     }
 
+    /**
+     * Return id for given index
+     *
+     * @param pos index
+     * @return id at index
+     */
     public int getId(int pos) {
         return mIds[pos];
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
index 98ee91b..93af8bc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
@@ -76,6 +76,20 @@
         adjustDrawToType();
     }
 
+    /**
+     * Setup the ImageScaling
+     *
+     * @param srcLeft src left
+     * @param srcTop src top
+     * @param srcRight src right
+     * @param srcBottom src bottom
+     * @param dstLeft destination left
+     * @param dstTop destination top
+     * @param dstRight destination right
+     * @param dstBottom destination bottom
+     * @param type type of scaling
+     * @param scale scale factor
+     */
     public void setup(
             float srcLeft,
             float srcTop,
@@ -215,6 +229,12 @@
         }
     }
 
+    /**
+     * Utility to map a string to the given type
+     *
+     * @param type
+     * @return
+     */
     @NonNull
     public static String typeToString(int type) {
         String[] typeString = {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
index b9aa881..257eb06 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
@@ -39,12 +39,20 @@
         }
     }
 
+    /** Clear the map */
     public void clear() {
         Arrays.fill(mKeys, NOT_PRESENT);
         mValues.clear();
         mSize = 0;
     }
 
+    /**
+     * Insert the value into the map with the given key
+     *
+     * @param key
+     * @param value
+     * @return
+     */
     @Nullable
     public T put(int key, @NonNull T value) {
         if (key == NOT_PRESENT) throw new IllegalArgumentException("Key cannot be NOT_PRESENT");
@@ -54,6 +62,12 @@
         return insert(key, value);
     }
 
+    /**
+     * Return the value associated with the given key
+     *
+     * @param key
+     * @return
+     */
     @Nullable
     public T get(int key) {
         int index = findKey(key);
@@ -62,6 +76,11 @@
         } else return mValues.get(index);
     }
 
+    /**
+     * Return the size of the map
+     *
+     * @return
+     */
     public int size() {
         return mSize;
     }
@@ -117,6 +136,12 @@
         }
     }
 
+    /**
+     * Remote the key from the map
+     *
+     * @param key
+     * @return
+     */
     @Nullable
     public T remove(int key) {
         int index = hash(key) % mKeys.length;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
index 0616cc7..1f98f62 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
@@ -41,18 +41,42 @@
     public static final float CLOSE_NAN = Utils.asNan(CLOSE);
     public static final float DONE_NAN = Utils.asNan(DONE);
 
+    /**
+     * Returns true if the float id is a system variable
+     *
+     * @param value the id encoded as float NaN
+     * @return
+     */
     public static boolean isSystemVariable(float value) {
         return (fromNaN(value) >> 20) == 0;
     }
 
+    /**
+     * Returns true if the float id is a normal variable
+     *
+     * @param value the id encoded as float NaN
+     * @return
+     */
     public static boolean isNormalVariable(float value) {
         return (fromNaN(value) >> 20) == 1;
     }
 
+    /**
+     * Returns true if the float id is a data variable
+     *
+     * @param value the id encoded as float NaN
+     * @return
+     */
     public static boolean isDataVariable(float value) {
         return (fromNaN(value) >> 20) == 2;
     }
 
+    /**
+     * Returns true if the float id is an operation variable
+     *
+     * @param value the id encoded as float NaN
+     * @return
+     */
     public static boolean isOperationVariable(float value) {
         return (fromNaN(value) >> 20) == 3;
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java
index cc6c2a6..928e9ea 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java
@@ -134,6 +134,12 @@
             return mDescription;
         }
 
+        /**
+         * Map int value to Role enum value
+         *
+         * @param i int value
+         * @return corresponding enum value
+         */
         public static Role fromInt(int i) {
             if (i < UNKNOWN.ordinal()) {
                 return Role.values()[i];
diff --git a/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java b/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java
new file mode 100644
index 0000000..2be8057
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2025 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.widget.remotecompose.core.serialize;
+
+import android.annotation.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/** Represents a serializer for a map */
+public interface MapSerializer {
+
+    /**
+     * Add a list entry to this map. The List values can be any primitive, List, Map, or
+     * Serializable
+     *
+     * @param key The key
+     * @param value The list
+     */
+    <T> void add(String key, @Nullable List<T> value);
+
+    /**
+     * Add a map entry to this map. The map values can be any primitive, List, Map, or Serializable
+     *
+     * @param key The key
+     * @param value The list
+     */
+    <T> void add(String key, @Nullable Map<String, T> value);
+
+    /**
+     * Adds any Serializable type to this map
+     *
+     * @param key The key
+     * @param value The Serializable
+     */
+    void add(String key, @Nullable Serializable value);
+
+    /**
+     * Adds a String entry
+     *
+     * @param key The key
+     * @param value The String
+     */
+    void add(String key, @Nullable String value);
+
+    /**
+     * Adds a Byte entry
+     *
+     * @param key The key
+     * @param value The Byte
+     */
+    void add(String key, @Nullable Byte value);
+
+    /**
+     * Adds a Short entry
+     *
+     * @param key The key
+     * @param value The Short
+     */
+    void add(String key, @Nullable Short value);
+
+    /**
+     * Adds an Integer entry
+     *
+     * @param key The key
+     * @param value The Integer
+     */
+    void add(String key, @Nullable Integer value);
+
+    /**
+     * Adds a Long entry
+     *
+     * @param key The key
+     * @param value The Long
+     */
+    void add(String key, @Nullable Long value);
+
+    /**
+     * Adds a Float entry
+     *
+     * @param key The key
+     * @param value The Float
+     */
+    void add(String key, @Nullable Float value);
+
+    /**
+     * Adds a Double entry
+     *
+     * @param key The key
+     * @param value The Double
+     */
+    void add(String key, @Nullable Double value);
+
+    /**
+     * Adds a Boolean entry
+     *
+     * @param key The key
+     * @param value The Boolean
+     */
+    void add(String key, @Nullable Boolean value);
+
+    /**
+     * Adds a Enum entry
+     *
+     * @param key The key
+     * @param value The Enum
+     */
+    <T extends Enum<T>> void add(String key, @Nullable Enum<T> value);
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/core/java/com/android/internal/widget/remotecompose/core/serialize/Serializable.java
similarity index 64%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
copy to core/java/com/android/internal/widget/remotecompose/core/serialize/Serializable.java
index aa262f9..820cdcc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/core/java/com/android/internal/widget/remotecompose/core/serialize/Serializable.java
@@ -13,15 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.internal.widget.remotecompose.core.serialize;
 
-package com.android.wm.shell.flicker.utils
+/** Implementation for any class that wants to serialize itself */
+public interface Serializable {
 
-import android.app.Instrumentation
-
-object RecentTasksUtils {
-    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
-        instrumentation.uiAutomation.executeShellCommand(
-            "dumpsys activity service SystemUIService WMShell recents clearAll"
-        )
-    }
-}
\ No newline at end of file
+    /**
+     * Called when this class is to be serialized
+     *
+     * @param serializer Interface to the serializer
+     */
+    void serialize(MapSerializer serializer);
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index b17e3dc..77f4b6a 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -39,12 +39,13 @@
 import com.android.internal.widget.remotecompose.accessibility.RemoteComposeTouchHelper;
 import com.android.internal.widget.remotecompose.core.CoreDocument;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.RemoteContextAware;
 import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
 import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
 import com.android.internal.widget.remotecompose.player.platform.RemoteComposeCanvas;
 
 /** A view to to display and play RemoteCompose documents */
-public class RemoteComposePlayer extends FrameLayout {
+public class RemoteComposePlayer extends FrameLayout implements RemoteContextAware {
     private RemoteComposeCanvas mInner;
 
     private static final int MAX_SUPPORTED_MAJOR_VERSION = MAJOR_VERSION;
@@ -65,6 +66,11 @@
         init(context, attrs, defStyleAttr);
     }
 
+    @Override
+    public RemoteContext getRemoteContext() {
+        return mInner.getRemoteContext();
+    }
+
     /**
      * @inheritDoc
      */
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index 9d385dd..14349b0 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -185,7 +185,7 @@
 
     @Override
     public void runAction(int id, @NonNull String metadata) {
-        mDocument.performClick(id);
+        mDocument.performClick(this, id);
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 334ba62..f76794f 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -147,7 +147,7 @@
                 param.leftMargin = (int) area.getLeft();
                 param.topMargin = (int) area.getTop();
                 viewArea.setOnClickListener(
-                        view1 -> mDocument.getDocument().performClick(area.getId()));
+                        view1 -> mDocument.getDocument().performClick(mARContext, area.getId()));
                 addView(viewArea, param);
             }
             if (!clickAreas.isEmpty()) {
@@ -303,6 +303,10 @@
         mARContext.setUseChoreographer(value);
     }
 
+    public RemoteContext getRemoteContext() {
+        return mARContext;
+    }
+
     public interface ClickCallbacks {
         void click(int id, String metadata);
     }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 3afe27e..a2f4ca2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -280,6 +280,7 @@
             ],
 
             static_libs: [
+                "android.os.flags-aconfig-cc",
                 "libasync_safe",
                 "libbinderthreadstateutils",
                 "libdmabufinfo",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index aea1734..5c0b720 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -22,6 +22,7 @@
 #include <android-base/parsebool.h>
 #include <android-base/properties.h>
 #include <android/graphics/jni_runtime.h>
+#include <android_os.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <assert.h>
 #include <binder/IBinder.h>
@@ -893,9 +894,13 @@
                        madviseWillNeedFileSizeOdex,
                        "-XMadviseWillNeedOdexFileSize:");
 
-    parseRuntimeOption("dalvik.vm.madvise.artfile.size",
-                       madviseWillNeedFileSizeArt,
-                       "-XMadviseWillNeedArtFileSize:");
+    // Historically, dalvik.vm.madvise.artfile.size was set to UINT_MAX by default. With the
+    // disable_madvise_art_default flag rollout, we use this default only when the flag is disabled.
+    // TODO(b/382110550): Remove this property/flag entirely after validating and ramping.
+    const char* madvise_artfile_size_default =
+            android::os::disable_madvise_artfile_default() ? "" : "4294967295";
+    parseRuntimeOption("dalvik.vm.madvise.artfile.size", madviseWillNeedFileSizeArt,
+                       "-XMadviseWillNeedArtFileSize:", madvise_artfile_size_default);
 
     /*
      * Profile related options.
diff --git a/core/jni/android_app_PropertyInvalidatedCache.h b/core/jni/android_app_PropertyInvalidatedCache.h
index 00aa281..54a4ac6 100644
--- a/core/jni/android_app_PropertyInvalidatedCache.h
+++ b/core/jni/android_app_PropertyInvalidatedCache.h
@@ -147,10 +147,12 @@
     }
 };
 
-// The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes.  This is
-// more than enough for system_server PropertyInvalidatedCache support.  The configuration
-// values are not defined as visible constants.  Clients should use the accessors on the
-// SystemCacheNonce instance if they need the sizing parameters.
-typedef CacheNonce</* max nonce */ 64, /* byte block size */ 8192> SystemCacheNonce;
+// The CacheNonce for system server.  The configuration values are not defined as visible
+// constants.  Clients should use the accessors on the SystemCacheNonce instance if they need
+// the sizing parameters.
+
+// LINT.IfChange(system_nonce_config)
+typedef CacheNonce</* max nonce */ 128, /* byte block size */ 8192> SystemCacheNonce;
+// LINT.ThenChange(/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java:system_nonce_config)
 
 } // namespace android.app.PropertyInvalidatedCache
diff --git a/core/jni/android_hardware_display_DisplayTopology.cpp b/core/jni/android_hardware_display_DisplayTopology.cpp
index d9e802d..a16f3c3 100644
--- a/core/jni/android_hardware_display_DisplayTopology.cpp
+++ b/core/jni/android_hardware_display_DisplayTopology.cpp
@@ -35,6 +35,7 @@
 static struct {
     jclass clazz;
     jfieldID displayId;
+    jfieldID density;
     jfieldID adjacentDisplays;
 } gDisplayTopologyGraphNodeClassInfo;
 
@@ -42,7 +43,7 @@
     jclass clazz;
     jfieldID displayId;
     jfieldID position;
-    jfieldID offsetPx;
+    jfieldID offsetDp;
 } gDisplayTopologyGraphAdjacentDisplayClassInfo;
 
 // ----------------------------------------------------------------------------
@@ -55,19 +56,23 @@
     adjacentDisplay->position = static_cast<DisplayTopologyPosition>(
             env->GetIntField(adjacentDisplayObj,
                              gDisplayTopologyGraphAdjacentDisplayClassInfo.position));
-    adjacentDisplay->offsetPx =
+    adjacentDisplay->offsetDp =
             env->GetFloatField(adjacentDisplayObj,
-                               gDisplayTopologyGraphAdjacentDisplayClassInfo.offsetPx);
+                               gDisplayTopologyGraphAdjacentDisplayClassInfo.offsetDp);
     return OK;
 }
 
 status_t android_hardware_display_DisplayTopologyGraphNode_toNative(
         JNIEnv* env, jobject nodeObj,
         std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&
-                graph) {
+                graph,
+        std::unordered_map<ui::LogicalDisplayId, int>& displaysDensity) {
     ui::LogicalDisplayId displayId = ui::LogicalDisplayId{
             env->GetIntField(nodeObj, gDisplayTopologyGraphNodeClassInfo.displayId)};
 
+    displaysDensity[displayId] =
+            env->GetIntField(nodeObj, gDisplayTopologyGraphNodeClassInfo.density);
+
     jobjectArray adjacentDisplaysArray = static_cast<jobjectArray>(
             env->GetObjectField(nodeObj, gDisplayTopologyGraphNodeClassInfo.adjacentDisplays));
 
@@ -109,7 +114,8 @@
             }
 
             android_hardware_display_DisplayTopologyGraphNode_toNative(env, nodeObj.get(),
-                                                                       topology.graph);
+                                                                       topology.graph,
+                                                                       topology.displaysDensity);
         }
     }
     return topology;
@@ -132,6 +138,8 @@
     gDisplayTopologyGraphNodeClassInfo.clazz = MakeGlobalRefOrDie(env, displayNodeClazz);
     gDisplayTopologyGraphNodeClassInfo.displayId =
             GetFieldIDOrDie(env, gDisplayTopologyGraphNodeClassInfo.clazz, "displayId", "I");
+    gDisplayTopologyGraphNodeClassInfo.density =
+            GetFieldIDOrDie(env, gDisplayTopologyGraphNodeClassInfo.clazz, "density", "I");
     gDisplayTopologyGraphNodeClassInfo.adjacentDisplays =
             GetFieldIDOrDie(env, gDisplayTopologyGraphNodeClassInfo.clazz, "adjacentDisplays",
                             "[Landroid/hardware/display/DisplayTopologyGraph$AdjacentDisplay;");
@@ -146,8 +154,8 @@
     gDisplayTopologyGraphAdjacentDisplayClassInfo.position =
             GetFieldIDOrDie(env, gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz, "position",
                             "I");
-    gDisplayTopologyGraphAdjacentDisplayClassInfo.offsetPx =
-            GetFieldIDOrDie(env, gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz, "offsetPx",
+    gDisplayTopologyGraphAdjacentDisplayClassInfo.offsetDp =
+            GetFieldIDOrDie(env, gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz, "offsetDp",
                             "F");
     return 0;
 }
diff --git a/core/jni/android_os_PerfettoTrace.cpp b/core/jni/android_os_PerfettoTrace.cpp
index 962aefc..9bedfa2 100644
--- a/core/jni/android_os_PerfettoTrace.cpp
+++ b/core/jni/android_os_PerfettoTrace.cpp
@@ -24,9 +24,12 @@
 #include <nativehelper/scoped_primitive_array.h>
 #include <nativehelper/scoped_utf_chars.h>
 #include <nativehelper/utils.h>
+#include <tracing_perfetto.h>
 #include <tracing_sdk.h>
 
 namespace android {
+constexpr int kFlushTimeoutMs = 5000;
+
 template <typename T>
 inline static T* toPointer(jlong ptr) {
     return reinterpret_cast<T*>(static_cast<uintptr_t>(ptr));
@@ -51,6 +54,10 @@
     tracing_perfetto::activate_trigger(name_chars.c_str(), static_cast<uint32_t>(ttl_ms));
 }
 
+void android_os_PerfettoTrace_register(bool is_backend_in_process) {
+    tracing_perfetto::registerWithPerfetto(is_backend_in_process);
+}
+
 static jlong android_os_PerfettoTraceCategory_init(JNIEnv* env, jclass, jstring name, jstring tag,
                                                    jstring severity) {
     ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
@@ -85,6 +92,36 @@
     return toJLong(category->get());
 }
 
+static jlong android_os_PerfettoTrace_start_session(JNIEnv* env, jclass /* obj */,
+                                                    jboolean is_backend_in_process,
+                                                    jbyteArray config_bytes) {
+    jsize length = env->GetArrayLength(config_bytes);
+    std::vector<uint8_t> data;
+    data.reserve(length);
+    env->GetByteArrayRegion(config_bytes, 0, length, reinterpret_cast<jbyte*>(data.data()));
+
+    tracing_perfetto::Session* session =
+            new tracing_perfetto::Session(is_backend_in_process, data.data(), length);
+
+    return reinterpret_cast<long>(session);
+}
+
+static jbyteArray android_os_PerfettoTrace_stop_session([[maybe_unused]] JNIEnv* env,
+                                                        jclass /* obj */, jlong ptr) {
+    tracing_perfetto::Session* session = reinterpret_cast<tracing_perfetto::Session*>(ptr);
+
+    session->FlushBlocking(kFlushTimeoutMs);
+    session->StopBlocking();
+
+    std::vector<uint8_t> data = session->ReadBlocking();
+
+    delete session;
+
+    jbyteArray bytes = env->NewByteArray(data.size());
+    env->SetByteArrayRegion(bytes, 0, data.size(), reinterpret_cast<jbyte*>(data.data()));
+    return bytes;
+}
+
 static const JNINativeMethod gCategoryMethods[] = {
         {"native_init", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
          (void*)android_os_PerfettoTraceCategory_init},
@@ -101,7 +138,10 @@
          {"native_get_thread_track_uuid", "(J)J",
           (void*)android_os_PerfettoTrace_get_thread_track_uuid},
          {"native_activate_trigger", "(Ljava/lang/String;I)V",
-          (void*)android_os_PerfettoTrace_activate_trigger}};
+          (void*)android_os_PerfettoTrace_activate_trigger},
+         {"native_register", "(Z)V", (void*)android_os_PerfettoTrace_register},
+         {"native_start_session", "(Z[B)J", (void*)android_os_PerfettoTrace_start_session},
+         {"native_stop_session", "(J)[B", (void*)android_os_PerfettoTrace_stop_session}};
 
 int register_android_os_PerfettoTrace(JNIEnv* env) {
     int res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace", gTraceMethods,
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 21e056d..50618c5 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -131,10 +131,6 @@
     return tracing_perfetto::isTagEnabled(tag);
 }
 
-static void android_os_Trace_nativeRegisterWithPerfetto(JNIEnv* env) {
-    tracing_perfetto::registerWithPerfetto();
-}
-
 static const JNINativeMethod gTraceMethods[] = {
         /* name, signature, funcPtr */
         {"nativeSetAppTracingAllowed", "(Z)V", (void*)android_os_Trace_nativeSetAppTracingAllowed},
@@ -157,7 +153,6 @@
         {"nativeInstant", "(JLjava/lang/String;)V", (void*)android_os_Trace_nativeInstant},
         {"nativeInstantForTrack", "(JLjava/lang/String;Ljava/lang/String;)V",
          (void*)android_os_Trace_nativeInstantForTrack},
-        {"nativeRegisterWithPerfetto", "()V", (void*)android_os_Trace_nativeRegisterWithPerfetto},
 
         // ----------- @CriticalNative  ----------------
         {"nativeIsTagEnabled", "(J)Z", (void*)android_os_Trace_nativeIsTagEnabled},
diff --git a/core/proto/android/service/appwidget.proto b/core/proto/android/service/appwidget.proto
index fb90719..5dc90e0 100644
--- a/core/proto/android/service/appwidget.proto
+++ b/core/proto/android/service/appwidget.proto
@@ -39,6 +39,7 @@
     optional int32 maxWidth = 8;
     optional int32 maxHeight = 9;
     optional bool restoreCompleted = 10;
+    optional int32 views_bitmap_memory = 11;
 }
 
 // represents a set of widget previews for a particular provider
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index aad8f8a..005c14d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8308,26 +8308,9 @@
         android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
         android:protectionLevel="signature" />
 
-    <!-- Allows a trusted application to perform actions on behalf of users inside of
-         applications with privacy guarantees from the system.
-         <p>This permission is currently only granted to system packages in the
-         {@link android.app.role.SYSTEM_UI_INTELLIGENCE} role which complies with privacy
-         requirements outlined in the Android CDD section "9.8.6 Content Capture".
-         <p>Apps are not able to opt-out from caller having this permission.
-         <p>Protection level: internal|role
-         @SystemApi
-         @hide
-         @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)  -->
-    <permission android:name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED"
-        android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
-        android:protectionLevel="internal|role" />
-
     <!-- Allows an application to perform actions on behalf of users inside of
          applications.
          <p>This permission is currently only granted to privileged system apps.
-         <p>Apps contributing app functions can opt to disallow callers with this permission,
-         limiting to only callers with {@link android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED}
-         instead.
          <p>Protection level: internal|privileged
          @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)  -->
     <permission android:name="android.permission.EXECUTE_APP_FUNCTIONS"
diff --git a/core/res/res/layout/notification_2025_conversation_header.xml b/core/res/res/layout/notification_2025_conversation_header.xml
index 1bde173..75bd244 100644
--- a/core/res/res/layout/notification_2025_conversation_header.xml
+++ b/core/res/res/layout/notification_2025_conversation_header.xml
@@ -29,7 +29,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
-        android:textSize="16sp"
+        android:textSize="@dimen/notification_2025_title_text_size"
         android:singleLine="true"
         android:layout_weight="1"
         />
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index d29b7af..05458329 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -102,6 +102,7 @@
                     android:singleLine="true"
                     android:textAlignment="viewStart"
                     android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+                    android:textSize="@dimen/notification_2025_title_text_size"
                     />
 
                 <include layout="@layout/notification_2025_top_line_views" />
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 5beab50..9959b66 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -104,6 +104,7 @@
                     android:singleLine="true"
                     android:textAlignment="viewStart"
                     android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+                    android:textSize="@dimen/notification_2025_title_text_size"
                     />
 
                 <include layout="@layout/notification_2025_top_line_views" />
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index d7c3263..85ca124 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -130,6 +130,7 @@
                             android:singleLine="true"
                             android:textAlignment="viewStart"
                             android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+                            android:textSize="@dimen/notification_2025_title_text_size"
                             />
 
                         <include layout="@layout/notification_2025_top_line_views" />
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 1fdbe8c..6871291 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1069,19 +1069,19 @@
     <string name="lockscreen_access_pattern_cell_added_verbose" msgid="2931364927622563465">"تمت إضافة الخلية <xliff:g id="CELL_INDEX">%1$s</xliff:g>"</string>
     <string name="lockscreen_access_pattern_detected" msgid="3931150554035194012">"اكتمل النمط"</string>
     <string name="lockscreen_access_pattern_area" msgid="1288780416685002841">"منطقة النقش."</string>
-    <string name="keyguard_accessibility_widget_changed" msgid="7298011259508200234">"‏%1$s. الأداة %2$d من %3$d."</string>
-    <string name="keyguard_accessibility_add_widget" msgid="8245795023551343672">"إضافة أداة."</string>
+    <string name="keyguard_accessibility_widget_changed" msgid="7298011259508200234">"‏%1$s. التطبيق المصغَّر %2$d من %3$d."</string>
+    <string name="keyguard_accessibility_add_widget" msgid="8245795023551343672">"إضافة تطبيق مصغَّر."</string>
     <string name="keyguard_accessibility_widget_empty_slot" msgid="544239307077644480">"فارغة"</string>
     <string name="keyguard_accessibility_unlock_area_expanded" msgid="7768634718706488951">"تم توسيع منطقة فتح القفل."</string>
     <string name="keyguard_accessibility_unlock_area_collapsed" msgid="4729922043778400434">"تم تصغير منطقة فتح القفل."</string>
-    <string name="keyguard_accessibility_widget" msgid="6776892679715699875">"أداة <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+    <string name="keyguard_accessibility_widget" msgid="6776892679715699875">"التطبيق المصغَّر <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
     <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"محدد المستخدم"</string>
     <string name="keyguard_accessibility_status" msgid="6792745049712397237">"الحالة"</string>
     <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"الكاميرا"</string>
     <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"أدوات التحكم في الوسائط"</string>
-    <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"بدأت إعادة ترتيب الأدوات."</string>
-    <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"انتهت إعادة ترتيب الأدوات."</string>
-    <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"تم حذف أداة <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+    <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"بدأت إعادة ترتيب التطبيقات المصغَّرة."</string>
+    <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"انتهت إعادة ترتيب التطبيقات المصغَّرة."</string>
+    <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"تم حذف التطبيق المصغَّر <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
     <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"توسيع منطقة فتح القفل."</string>
     <string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"فتح القفل باستخدام التمرير."</string>
     <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"فتح القفل باستخدام النقش."</string>
@@ -1536,7 +1536,7 @@
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"البحث في كل الحِزم"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"يسمح هذا الإذن للتطبيق بعرض كل الحِزم المثبّتة."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"اضغط مرتين للتحكم في التكبير أو التصغير"</string>
-    <string name="gadget_host_error_inflating" msgid="2449961590495198720">"تعذرت إضافة أداة."</string>
+    <string name="gadget_host_error_inflating" msgid="2449961590495198720">"تعذرت إضافة تطبيق مصغَّر."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"تنفيذ"</string>
     <string name="ime_action_search" msgid="4501435960587287668">"بحث"</string>
     <string name="ime_action_send" msgid="8456843745664334138">"إرسال"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index d775f44..6f677e3 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -2219,7 +2219,7 @@
     <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"O Bluetooth permanecerá activado mentres se utilice o modo avión"</string>
     <string name="car_loading_profile" msgid="8219978381196748070">"Cargando"</string>
     <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # ficheiro}other{{file_name} + # ficheiros}}"</string>
-    <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Non hai recomendacións de persoas coas que compartir contido"</string>
+    <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Non hai recomendacións de persoas coas que compartir isto"</string>
     <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Lista de aplicacións"</string>
     <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Esta aplicación non está autorizada a realizar gravacións, pero podería capturar audio a través deste dispositivo USB."</string>
     <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Inicio"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index ffca868..cbc989e 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -2110,7 +2110,7 @@
     <string name="time_picker_minute_label" msgid="8307452311269824553">"דקה"</string>
     <string name="time_picker_header_text" msgid="9073802285051516688">"הגדרת שעה"</string>
     <string name="time_picker_input_error" msgid="8386271930742451034">"יש להזין שעה חוקית"</string>
-    <string name="time_picker_prompt_label" msgid="303588544656363889">"מהי השעה הנכונה"</string>
+    <string name="time_picker_prompt_label" msgid="303588544656363889">"צריך להקליד את השעה הרצויה"</string>
     <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"מעבר לשיטת קלט טקסט כדי להזין את השעה"</string>
     <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"מעבר למצב שעון לצורך הזנת השעה"</string>
     <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"אפשרויות מילוי אוטומטי"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 774e4ed..f2de857 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -147,7 +147,7 @@
     <string name="wfcSpnFormat_wifi_call" msgid="434016592539090004">"Повик преку WiFi"</string>
     <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Исклучено"</string>
     <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Повикувај преку Wi-Fi"</string>
-    <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Повикувај преку мобилна мрежа"</string>
+    <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Повикувајте преку мобилна мрежа"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само Wi-Fi"</string>
     <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
     <skip />
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 9dbfc1e..2c4b9f4 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -2109,7 +2109,7 @@
     <string name="time_picker_minute_label" msgid="8307452311269824553">"минут"</string>
     <string name="time_picker_header_text" msgid="9073802285051516688">"Цаг тохируулах"</string>
     <string name="time_picker_input_error" msgid="8386271930742451034">"Цагийг зөв оруулна уу"</string>
-    <string name="time_picker_prompt_label" msgid="303588544656363889">"Хугацааг бичнэ үү"</string>
+    <string name="time_picker_prompt_label" msgid="303588544656363889">"Цагийг бичиж оруулна уу"</string>
     <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Цагийг оруулахын тулд текст оруулах горимд шилжүүлнэ үү."</string>
     <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Цагийг оруулахын тулд цагийн горимд шилжүүлнэ үү."</string>
     <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Автоматаар бөглөх хэсгийн сонголт"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 750988a..63b017a 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1206,7 +1206,7 @@
     <string name="delete" msgid="1514113991712129054">"हटवा"</string>
     <string name="copyUrl" msgid="6229645005987260230">"URL कॉपी करा"</string>
     <string name="selectTextMode" msgid="3225108910999318778">"मजकूर निवडा"</string>
-    <string name="undo" msgid="3175318090002654673">"पूर्ववत करा"</string>
+    <string name="undo" msgid="3175318090002654673">"पहिल्यासारखे करा"</string>
     <string name="redo" msgid="7231448494008532233">"पुन्हा करा"</string>
     <string name="autofill" msgid="511224882647795296">"स्वयं-भरण"</string>
     <string name="textSelectionCABTitle" msgid="5151441579532476940">"मजकूर निवड"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index f7a25e9..2776e1c 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -2469,7 +2469,7 @@
     <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Vklopite »Samodejno izberi omrežje«"</string>
     <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"V nastavitvah vklopite možnost »Samodejno izberi omrežje«, da bo telefon lahko našel omrežje, ki deluje s satelitsko povezavo"</string>
     <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Vklopi"</string>
-    <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Nazaj"</string>
+    <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Pomik nazaj"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"V teku …"</string>
     <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS prek satelita je zdaj na voljo"</string>
     <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Reševalnim službam lahko pošljete sporočilo, če ni povezave z mobilnim omrežjem ali omrežjem Wi-Fi. Google Sporočila morajo biti privzeta aplikacija za sporočanje."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 38f9175..8c5acd4 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -2459,7 +2459,7 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"பாதுகாப்பிற்காக, திரைப் பகிர்வில் இருந்து ஆப்ஸ் உள்ளடக்கம் மறைக்கப்பட்டுள்ளது"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"சாட்டிலைட்டுடன் தானாக இணைக்கப்பட்டது"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"மொபைல்/வைஃபை நெட்வொர்க் இல்லாமல் நீங்கள் மெசேஜ்களை அனுப்பலாம் பெறலாம்"</string>
-    <string name="satellite_notification_summary_with_data" msgid="6486843676720429049">"சாட்டிலைட் மூலம் நீங்கள் மெசேஜ்களை அனுப்பலாம் பெறலாம், வரம்பிடப்பட்ட டேட்டாவைப் பயன்படுத்தலாம்"</string>
+    <string name="satellite_notification_summary_with_data" msgid="6486843676720429049">"சாட்டிலைட் மூலம் நீங்கள் மெசேஜ் அனுப்பலாம் பெறலாம், குறிப்பிட்ட அளவு டேட்டாவைப் பயன்படுத்தலாம்"</string>
     <string name="satellite_notification_manual_title" msgid="1097504441849466279">"சாட்டிலைட் மெசேஜிங்கைப் பயன்படுத்தவா?"</string>
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"மொபைல்/வைஃபை நெட்வொர்க் இல்லாமல் மெசேஜ்களை அனுப்பலாம், பெறலாம்"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ஆப்ஸைத் திறக்கவும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 2a99f75..8333b0b 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -2109,7 +2109,7 @@
     <string name="time_picker_minute_label" msgid="8307452311269824553">"నిమిషం"</string>
     <string name="time_picker_header_text" msgid="9073802285051516688">"సమయాన్ని సెట్ చేయండి"</string>
     <string name="time_picker_input_error" msgid="8386271930742451034">"చెల్లుబాటు అయ్యే సమయాన్ని నమోదు చేయండి"</string>
-    <string name="time_picker_prompt_label" msgid="303588544656363889">"సమయంలో టైప్ చేయండి"</string>
+    <string name="time_picker_prompt_label" msgid="303588544656363889">"టైమ్ ఎంట‌ర్ చేయండి"</string>
     <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"సమయాన్ని నమోదు చేయడం కోసం వచన నమోదు మోడ్‌కి మారండి."</string>
     <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"సమయాన్ని నమోదు చేయడం కోసం గడియారం మోడ్‌కు మారండి."</string>
     <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"స్వీయ పూరింపు ఎంపికలు"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index c5cad4b..7888f47 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -620,8 +620,8 @@
     <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允许该应用向附近的 WLAN 设备进行广播、连接到这些设备以及确定这些设备的相对位置"</string>
     <string name="permlab_ranging" msgid="2854543350668593390">"确定附近的设备之间的相对位置"</string>
     <string name="permdesc_ranging" msgid="6703905535621521710">"允许应用确定附近的设备之间的相对位置"</string>
-    <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首选 NFC 付款服务信息"</string>
-    <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允许应用获取首选 NFC 付款服务信息,例如注册的应用标识符和路线目的地。"</string>
+    <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首选 NFC 支付服务信息"</string>
+    <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允许应用获取首选 NFC 支付服务信息,例如注册的应用标识符和路线目的地。"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"控制近距离通信"</string>
     <string name="permdesc_nfc" msgid="8352737680695296741">"允许应用与近距离无线通信(NFC)标签、卡和读取器通信。"</string>
     <string name="permlab_nfcTransactionEvent" msgid="5868209446710407679">"安全元件事务事件"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b894d3a..a49e034 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2009,6 +2009,10 @@
     <!-- Component name of the built in wallpaper used to display bitmap wallpapers. This must not be null. -->
     <string name="image_wallpaper_component" translatable="false">com.android.systemui/com.android.systemui.wallpapers.ImageWallpaper</string>
 
+    <!-- Component name of the built in wallpaper that is used when the user-selected wallpaper is
+         incompatible with the display's resolution or aspect ratio. -->
+    <string name="fallback_wallpaper_component" translatable="false">com.android.systemui/com.android.systemui.wallpapers.GradientColorWallpaper</string>
+
     <!-- True if WallpaperService is enabled -->
     <bool name="config_enableWallpaperService">true</bool>
 
@@ -7241,6 +7245,9 @@
     <!-- Whether desktop mode is supported on the current device  -->
     <bool name="config_isDesktopModeSupported">false</bool>
 
+    <!-- Whether the developer option for desktop mode is supported on the current device  -->
+    <bool name="config_isDesktopModeDevOptionSupported">false</bool>
+
     <!-- Maximum number of active tasks on a given Desktop Windowing session. Set to 0 for unlimited. -->
     <integer name="config_maxDesktopWindowingActiveTasks">0</integer>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index d6b8704..5644cf9 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -577,6 +577,9 @@
     <dimen name="notification_text_size">14sp</dimen>
     <!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title) -->
     <dimen name="notification_title_text_size">14sp</dimen>
+    <!-- Size of notification text titles, 2025 redesign version (see TextAppearance.StatusBar.EventContent.Title) -->
+    <!-- TODO: b/378660052 - When inlining the redesign flag, this should be updated directly in TextAppearance.DeviceDefault.Notification.Title -->
+    <dimen name="notification_2025_title_text_size">16sp</dimen>
     <!-- Size of big notification text titles (see TextAppearance.StatusBar.EventContent.BigTitle) -->
     <dimen name="notification_big_title_text_size">16sp</dimen>
     <!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info, Time) -->
diff --git a/core/res/res/values/public-final.xml b/core/res/res/values/public-final.xml
index d421944..d8e8931 100644
--- a/core/res/res/values/public-final.xml
+++ b/core/res/res/values/public-final.xml
@@ -3922,4 +3922,86 @@
   <public type="color" name="system_error_900" id="0x010600d0" />
   <public type="color" name="system_error_1000" id="0x010600d1" />
 
+  <!-- ===============================================================
+    Resources added in version NEXT of the platform
+
+    NOTE: After this version of the platform is forked, changes cannot be made to the root
+    branch's groups for that release. Only merge changes to the forked platform branch.
+    =============================================================== -->
+  <eat-comment/>
+
+  <staging-public-group-final type="attr" first-id="0x01b70000">
+    <public name="removed_" />
+    <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") -->
+    <public name="adServiceTypes" />
+    <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") -->
+    <public name="languageSettingsActivity"/>
+    <!-- @FlaggedApi(android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM) -->
+    <public name="dreamCategory"/>
+    <!-- @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled")
+         @hide @SystemApi -->
+    <public name="backgroundPermission"/>
+    <!-- @FlaggedApi(android.view.accessibility.supplemental_description) -->
+    <public name="supplementalDescription"/>
+    <!-- @FlaggedApi("android.security.enable_intent_matching_flags") -->
+    <public name="intentMatchingFlags"/>
+    <!-- @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API) -->
+    <public name="layoutLabel"/>
+    <public name="removed_" />
+    <public name="removed_" />
+    <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
+    <public name="pageSizeCompat" />
+    <!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
+    <public name="wantsRoleHolderPriority"/>
+    <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
+    <public name="minSdkVersionFull"/>
+    <public name="removed_" />
+    <public name="removed_" />
+    <public name="removed_" />
+    <public name="removed_" />
+  </staging-public-group-final>
+
+  <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") -->
+  <public type="attr" name="adServiceTypes" id="0x010106a4" />
+  <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") -->
+  <public type="attr" name="languageSettingsActivity" id="0x010106a5" />
+  <!-- @FlaggedApi(android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM) -->
+  <public type="attr" name="dreamCategory" id="0x010106a6" />
+  <!-- @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled")
+       @hide @SystemApi -->
+  <public type="attr" name="backgroundPermission" id="0x010106a7" />
+  <!-- @FlaggedApi(android.view.accessibility.supplemental_description) -->
+  <public type="attr" name="supplementalDescription" id="0x010106a8" />
+  <!-- @FlaggedApi("android.security.enable_intent_matching_flags") -->
+  <public type="attr" name="intentMatchingFlags" id="0x010106a9" />
+  <!-- @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API) -->
+  <public type="attr" name="layoutLabel" id="0x010106aa" />
+  <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
+  <public type="attr" name="pageSizeCompat" id="0x010106ab" />
+  <!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
+  <public type="attr" name="wantsRoleHolderPriority" id="0x010106ac" />
+  <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
+  <public type="attr" name="minSdkVersionFull" id="0x010106ad" />
+
+  <staging-public-group-final type="string" first-id="0x01b40000">
+    <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+         @hide @SystemApi -->
+    <public name="config_systemDependencyInstaller" />
+    <!-- @hide @SystemApi -->
+    <public name="removed_config_defaultReservedForTestingProfileGroupExclusivity" />
+    <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_VENDOR_INTELLIGENCE_ROLE_ENABLED)
+         @hide @SystemApi -->
+    <public name="config_systemVendorIntelligence" />
+    <public name="removed_" />
+    <public name="removed_" />
+    <public name="removed_" />
+  </staging-public-group-final>
+
+  <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+       @hide @SystemApi -->
+  <public type="string" name="config_systemDependencyInstaller" id="0x0104004a" />
+  <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_VENDOR_INTELLIGENCE_ROLE_ENABLED)
+       @hide @SystemApi -->
+  <public type="string" name="config_systemVendorIntelligence" id="0x0104004b" />
+
 </resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index a0c4c13..2d411d0 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -109,34 +109,13 @@
     =============================================================== -->
   <eat-comment/>
 
-  <staging-public-group type="attr" first-id="0x01b70000">
+  <staging-public-group type="attr" first-id="0x01b30000">
       <!-- @FlaggedApi("android.content.pm.sdk_lib_independence") -->
     <public name="optional"/>
-    <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") -->
-    <public name="adServiceTypes" />
-    <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") -->
-    <public name="languageSettingsActivity"/>
-    <!-- @FlaggedApi(android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM) -->
-    <public name="dreamCategory"/>
-    <!-- @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled")
-         @hide @SystemApi -->
-    <public name="backgroundPermission"/>
-    <!-- @FlaggedApi(android.view.accessibility.supplemental_description) -->
-    <public name="supplementalDescription"/>
-    <!-- @FlaggedApi("android.security.enable_intent_matching_flags") -->
-    <public name="intentMatchingFlags"/>
-    <!-- @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API) -->
-    <public name="layoutLabel"/>
     <!-- @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->
     <public name="alternateLauncherIcons"/>
     <!-- @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->
     <public name="alternateLauncherLabels"/>
-    <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
-    <public name="pageSizeCompat" />
-    <!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
-    <public name="wantsRoleHolderPriority"/>
-    <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
-    <public name="minSdkVersionFull"/>
     <!-- @hide Only for device overlay to use this. -->
     <public name="pointerIconVectorFill"/>
     <!-- @hide Only for device overlay to use this. -->
@@ -147,39 +126,27 @@
     <public name="pointerIconVectorStrokeInverse"/>
   </staging-public-group>
 
-  <staging-public-group type="id" first-id="0x01b60000">
+  <staging-public-group type="id" first-id="0x01b20000">
     <!-- @FlaggedApi(android.appwidget.flags.Flags.FLAG_ENGAGEMENT_METRICS) -->
     <public name="remoteViewsMetricsId"/>
   </staging-public-group>
 
-  <staging-public-group type="style" first-id="0x01b50000">
+  <staging-public-group type="style" first-id="0x01b10000">
   </staging-public-group>
 
-  <staging-public-group type="string" first-id="0x01b40000">
-    <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
-         @hide @SystemApi -->
-    <public name="config_systemDependencyInstaller" />
-    <!-- @hide @SystemApi -->
-    <public name="removed_config_defaultReservedForTestingProfileGroupExclusivity" />
-    <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_VENDOR_INTELLIGENCE_ROLE_ENABLED)
-         @hide @SystemApi -->
-    <public name="config_systemVendorIntelligence" />
-
+  <staging-public-group type="string" first-id="0x01b00000">
     <!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
      @hide @SystemApi -->
     <public name="config_defaultOnDeviceIntelligenceService" />
-
     <!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
      @hide @SystemApi -->
     <public name="config_defaultOnDeviceSandboxedInferenceService" />
-
     <!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
      @hide @SystemApi -->
     <public name="config_defaultOnDeviceIntelligenceDeviceConfigNamespace" />
-
   </staging-public-group>
 
-  <staging-public-group type="dimen" first-id="0x01b30000">
+  <staging-public-group type="dimen" first-id="0x01af0000">
     <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_MOTION_TOKENS)-->
     <public name="config_motionStandardFastSpatialDamping"/>
     <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_MOTION_TOKENS)-->
@@ -216,7 +183,7 @@
     <public name="config_shapeCornerRadiusXlarge"/>
   </staging-public-group>
 
-  <staging-public-group type="color" first-id="0x01b20000">
+  <staging-public-group type="color" first-id="0x01ae0000">
     <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
     <public name="system_inverse_on_surface_light"/>
     <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
@@ -243,28 +210,28 @@
     <public name="system_surface_tint_dark"/>
   </staging-public-group>
 
-  <staging-public-group type="array" first-id="0x01b10000">
+  <staging-public-group type="array" first-id="0x01ad0000">
   </staging-public-group>
 
-  <staging-public-group type="drawable" first-id="0x01b00000">
+  <staging-public-group type="drawable" first-id="0x01ac0000">
   </staging-public-group>
 
-  <staging-public-group type="layout" first-id="0x01af0000">
+  <staging-public-group type="layout" first-id="0x01ab0000">
   </staging-public-group>
 
-  <staging-public-group type="anim" first-id="0x01ae0000">
+  <staging-public-group type="anim" first-id="0x01aa0000">
   </staging-public-group>
 
-  <staging-public-group type="animator" first-id="0x01ad0000">
+  <staging-public-group type="animator" first-id="0x01a90000">
   </staging-public-group>
 
-  <staging-public-group type="interpolator" first-id="0x01ac0000">
+  <staging-public-group type="interpolator" first-id="0x01a80000">
   </staging-public-group>
 
-  <staging-public-group type="mipmap" first-id="0x01ab0000">
+  <staging-public-group type="mipmap" first-id="0x01a70000">
   </staging-public-group>
 
-  <staging-public-group type="integer" first-id="0x01aa0000">
+  <staging-public-group type="integer" first-id="0x01a60000">
     <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_MOTION_TOKENS)-->
     <public name="config_motionStandardFastSpatialStiffness"/>
     <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_MOTION_TOKENS)-->
@@ -291,16 +258,16 @@
     <public name="config_motionExpressiveSlowEffectStiffness"/>
   </staging-public-group>
 
-  <staging-public-group type="transition" first-id="0x01a90000">
+  <staging-public-group type="transition" first-id="0x01a50000">
   </staging-public-group>
 
-  <staging-public-group type="raw" first-id="0x01a80000">
+  <staging-public-group type="raw" first-id="0x01a40000">
   </staging-public-group>
 
-  <staging-public-group type="bool" first-id="0x01a70000">
+  <staging-public-group type="bool" first-id="0x01a30000">
   </staging-public-group>
 
-  <staging-public-group type="fraction" first-id="0x01a60000">
+  <staging-public-group type="fraction" first-id="0x01a20000">
   </staging-public-group>
 
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8315e3c..8bb3c99 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,7 @@
   <java-symbol type="dimen" name="notification_text_size" />
   <java-symbol type="dimen" name="notification_title_text_size" />
   <java-symbol type="dimen" name="notification_subtext_size" />
+  <java-symbol type="dimen" name="notification_2025_title_text_size" />
   <java-symbol type="dimen" name="notification_top_pad" />
   <java-symbol type="dimen" name="notification_top_pad_narrow" />
   <java-symbol type="dimen" name="notification_top_pad_large_text" />
@@ -2270,6 +2271,7 @@
   <java-symbol type="string" name="heavy_weight_notification" />
   <java-symbol type="string" name="heavy_weight_notification_detail" />
   <java-symbol type="string" name="image_wallpaper_component" />
+  <java-symbol type="string" name="fallback_wallpaper_component" />
   <java-symbol type="string" name="input_method_binding_label" />
   <java-symbol type="string" name="input_method_ime_switch_long_click_action_desc" />
   <java-symbol type="string" name="launch_warning_original" />
@@ -5708,6 +5710,9 @@
   <!-- Whether desktop mode is supported on the current device  -->
   <java-symbol type="bool" name="config_isDesktopModeSupported" />
 
+  <!-- Whether the developer option for desktop mode is supported on the current device  -->
+  <java-symbol type="bool" name="config_isDesktopModeDevOptionSupported" />
+
   <!-- Maximum number of active tasks on a given Desktop Windowing session. Set to 0 for unlimited. -->
   <java-symbol type="integer" name="config_maxDesktopWindowingActiveTasks"/>
 
diff --git a/core/tests/FileSystemUtilsTest/Android.bp b/core/tests/FileSystemUtilsTest/Android.bp
index 962ff3c..f4d9252 100644
--- a/core/tests/FileSystemUtilsTest/Android.bp
+++ b/core/tests/FileSystemUtilsTest/Android.bp
@@ -36,10 +36,10 @@
     ldflags: ["-z max-page-size=0x1000"],
 }
 
-android_test_helper_app {
-    name: "app_with_4kb_elf",
+java_defaults {
+    name: "app_with_4kb_elf_defaults",
     srcs: ["app_with_4kb_elf/src/**/*.java"],
-    manifest: "app_with_4kb_elf/app_with_4kb_elf.xml",
+    resource_dirs: ["app_with_4kb_elf/res"],
     compile_multilib: "64",
     jni_libs: [
         "libpunchtest_4kb",
@@ -47,7 +47,36 @@
     static_libs: [
         "androidx.test.rules",
         "platform-test-annotations",
+        "androidx.test.uiautomator_uiautomator",
+        "sysui-helper",
     ],
+}
+
+android_test_helper_app {
+    name: "app_with_4kb_elf",
+    defaults: ["app_with_4kb_elf_defaults"],
+    manifest: "app_with_4kb_elf/app_with_4kb_elf.xml",
+    use_embedded_native_libs: true,
+}
+
+android_test_helper_app {
+    name: "app_with_4kb_compressed_elf",
+    defaults: ["app_with_4kb_elf_defaults"],
+    manifest: "app_with_4kb_elf/app_with_4kb_elf.xml",
+    use_embedded_native_libs: false,
+}
+
+android_test_helper_app {
+    name: "page_size_compat_disabled_app",
+    defaults: ["app_with_4kb_elf_defaults"],
+    manifest: "app_with_4kb_elf/page_size_compat_disabled.xml",
+    use_embedded_native_libs: true,
+}
+
+android_test_helper_app {
+    name: "app_with_4kb_elf_no_override",
+    defaults: ["app_with_4kb_elf_defaults"],
+    manifest: "app_with_4kb_elf/app_with_4kb_no_override.xml",
     use_embedded_native_libs: true,
 }
 
@@ -99,6 +128,9 @@
         ":embedded_native_libs_test_app",
         ":extract_native_libs_test_app",
         ":app_with_4kb_elf",
+        ":page_size_compat_disabled_app",
+        ":app_with_4kb_compressed_elf",
+        ":app_with_4kb_elf_no_override",
     ],
     test_suites: ["general-tests"],
     test_config: "AndroidTest.xml",
diff --git a/core/tests/FileSystemUtilsTest/AndroidTest.xml b/core/tests/FileSystemUtilsTest/AndroidTest.xml
index 651a7ca..27f49b2 100644
--- a/core/tests/FileSystemUtilsTest/AndroidTest.xml
+++ b/core/tests/FileSystemUtilsTest/AndroidTest.xml
@@ -22,7 +22,6 @@
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="embedded_native_libs_test_app.apk" />
         <option name="test-file-name" value="extract_native_libs_test_app.apk" />
-        <option name="test-file-name" value="app_with_4kb_elf.apk" />
     </target_preparer>
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml
index b9d6d4d..d7a3733 100644
--- a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml
@@ -18,7 +18,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.test.pagesizecompat">
     <application
-        android:extractNativeLibs="false"
         android:pageSizeCompat="enabled">
         <uses-library android:name="android.test.runner"/>
         <activity android:name=".MainActivity"
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_no_override.xml b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_no_override.xml
new file mode 100644
index 0000000..b0b5204
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_no_override.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.test.pagesizecompat">
+    <application
+        android:label="PageSizeCompatTestApp">
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name=".MainActivity"
+                  android:exported="true"
+                  android:label="Home page"
+                  android:process=":NewProcess">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+    </application>
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.test.pagesizecompat"/>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/page_size_compat_disabled.xml b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/page_size_compat_disabled.xml
new file mode 100644
index 0000000..641c5e7
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/page_size_compat_disabled.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.test.pagesizecompat">
+    <application
+        android:pageSizeCompat="disabled">
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name=".MainActivity"
+                  android:exported="true"
+                  android:process=":NewProcess">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+    </application>
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.test.pagesizecompat"/>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/res/layout/hello.xml b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/res/layout/hello.xml
new file mode 100644
index 0000000..473f3f9
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/res/layout/hello.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"
+              android:keepScreenOn="true">
+    <TextView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:text="this is a test activity"
+    />
+</LinearLayout>
+
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java
index 893f9cd..5d8d808 100644
--- a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
+import android.view.View;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -43,6 +44,8 @@
     @Override
     public void onCreate(Bundle savedOnstanceState) {
         super.onCreate(savedOnstanceState);
+        View view = getLayoutInflater().inflate(R.layout.hello, null);
+        setContentView(view);
 
         Intent received =  getIntent();
         int op1 = received.getIntExtra(KEY_OPERAND_1, -1);
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java
index 9cbe414a..7d05c64 100644
--- a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java
@@ -16,6 +16,8 @@
 
 package android.test.pagesizecompat;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -23,6 +25,10 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -33,9 +39,10 @@
 
 @RunWith(AndroidJUnit4.class)
 public class PageSizeCompatTest {
+    private static final String WARNING_TEXT = "PageSizeCompatTestApp";
+    private static final long TIMEOUT = 5000;
 
-    @Test
-    public void testPageSizeCompat_embedded4KbLib() throws Exception {
+    public void testPageSizeCompat_appLaunch(boolean shouldPass) throws Exception {
         Context context = InstrumentationRegistry.getContext();
         CountDownLatch receivedSignal = new CountDownLatch(1);
 
@@ -62,6 +69,30 @@
         launchIntent.putExtra(MainActivity.KEY_OPERAND_2, op2);
         context.startActivity(launchIntent);
 
-        Assert.assertTrue("Failed to launch app", receivedSignal.await(10, TimeUnit.SECONDS));
+        UiDevice device = UiDevice.getInstance(getInstrumentation());
+        device.waitForWindowUpdate(null, TIMEOUT);
+
+        Assert.assertEquals(receivedSignal.await(10, TimeUnit.SECONDS), shouldPass);
+    }
+
+    @Test
+    public void testPageSizeCompat_compatEnabled() throws Exception {
+        testPageSizeCompat_appLaunch(true);
+    }
+
+    @Test
+    public void testPageSizeCompat_compatDisabled() throws Exception {
+        testPageSizeCompat_appLaunch(false);
+    }
+
+    @Test
+    public void testPageSizeCompat_compatByAlignmentChecks() throws Exception {
+        testPageSizeCompat_appLaunch(true);
+
+        //verify warning dialog
+        UiDevice device = UiDevice.getInstance(getInstrumentation());
+        device.waitForWindowUpdate(null, TIMEOUT);
+        UiObject2 targetObject = device.wait(Until.findObject(By.text(WARNING_TEXT)), TIMEOUT);
+        Assert.assertTrue(targetObject != null);
     }
 }
diff --git a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
index aed907a..208d74e 100644
--- a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
+++ b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
@@ -17,10 +17,12 @@
 package com.android.internal.content;
 
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.platform.test.annotations.AppModeFull;
 
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.targetprep.TargetSetupError;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -29,6 +31,12 @@
 
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class FileSystemUtilsTest extends BaseHostJUnit4Test {
+    private static final String PAGE_SIZE_COMPAT_ENABLED = "app_with_4kb_elf.apk";
+    private static final String PAGE_SIZE_COMPAT_DISABLED = "page_size_compat_disabled_app.apk";
+    private static final String PAGE_SIZE_COMPAT_ENABLED_COMPRESSED_ELF =
+            "app_with_4kb_compressed_elf.apk";
+    private static final String PAGE_SIZE_COMPAT_ENABLED_BY_PLATFORM =
+            "app_with_4kb_elf_no_override.apk";
 
     @Test
     @AppModeFull
@@ -48,12 +56,50 @@
         runDeviceTests(appPackage, appPackage + "." + testName);
     }
 
-    @Test
-    @AppModeFull
-    public void runAppWith4KbLib_overrideCompatMode() throws DeviceNotAvailableException {
+    private void runPageSizeCompatTest(String appName, String testMethodName)
+            throws DeviceNotAvailableException, TargetSetupError {
+        getDevice().enableAdbRoot();
+        String result = getDevice().executeShellCommand("getconf PAGE_SIZE");
+        assumeTrue("16384".equals(result.strip()));
+        installPackage(appName, "-r");
         String appPackage = "android.test.pagesizecompat";
         String testName = "PageSizeCompatTest";
         assertTrue(isPackageInstalled(appPackage));
-        runDeviceTests(appPackage, appPackage + "." + testName);
+        assertTrue(runDeviceTests(appPackage, appPackage + "." + testName,
+                testMethodName));
+        uninstallPackage(appPackage);
+    }
+
+    @Test
+    @AppModeFull
+    public void runAppWith4KbLib_overrideCompatMode()
+            throws DeviceNotAvailableException, TargetSetupError {
+        runPageSizeCompatTest(PAGE_SIZE_COMPAT_ENABLED, "testPageSizeCompat_compatEnabled");
+    }
+
+    @Test
+    @AppModeFull
+    public void runAppWith4KbCompressedLib_overrideCompatMode()
+            throws DeviceNotAvailableException, TargetSetupError {
+        runPageSizeCompatTest(PAGE_SIZE_COMPAT_ENABLED_COMPRESSED_ELF,
+                "testPageSizeCompat_compatEnabled");
+    }
+
+    @Test
+    @AppModeFull
+    public void runAppWith4KbLib_disabledCompatMode()
+            throws DeviceNotAvailableException, TargetSetupError {
+        // This test is expected to fail since compat is disabled in manifest
+        runPageSizeCompatTest(PAGE_SIZE_COMPAT_DISABLED,
+                "testPageSizeCompat_compatDisabled");
+    }
+
+    @Test
+    @AppModeFull
+    public void runAppWith4KbLib_compatByAlignmentChecks()
+            throws DeviceNotAvailableException, TargetSetupError {
+        // This test is expected to fail since compat is disabled in manifest
+        runPageSizeCompatTest(PAGE_SIZE_COMPAT_ENABLED_BY_PLATFORM,
+                "testPageSizeCompat_compatByAlignmentChecks");
     }
 }
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 1b6746c..c06ad64 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -122,7 +122,6 @@
         "android.view.flags-aconfig-java",
     ],
     jni_libs: [
-        "libperfetto_trace_test_jni",
         "libpowermanagertest_jni",
         "libviewRootImplTest_jni",
         "libworksourceparceltest_jni",
diff --git a/core/tests/coretests/jni/Android.bp b/core/tests/coretests/jni/Android.bp
index 798ec90..d6379ca 100644
--- a/core/tests/coretests/jni/Android.bp
+++ b/core/tests/coretests/jni/Android.bp
@@ -111,27 +111,3 @@
     ],
     gtest: false,
 }
-
-cc_test_library {
-    name: "libperfetto_trace_test_jni",
-    srcs: [
-        "PerfettoTraceTest.cpp",
-    ],
-    static_libs: [
-        "perfetto_trace_protos",
-        "libtracing_perfetto_test_utils",
-    ],
-    shared_libs: [
-        "liblog",
-        "libnativehelper",
-        "libperfetto_c",
-        "libprotobuf-cpp-lite",
-        "libtracing_perfetto",
-    ],
-    stl: "libc++_static",
-    cflags: [
-        "-Werror",
-        "-Wall",
-    ],
-    gtest: false,
-}
diff --git a/core/tests/coretests/jni/PerfettoTraceTest.cpp b/core/tests/coretests/jni/PerfettoTraceTest.cpp
deleted file mode 100644
index 41d02ed7..0000000
--- a/core/tests/coretests/jni/PerfettoTraceTest.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.
- */
-
-// #define LOG_NDEBUG 0
-#define LOG_TAG "PerfettoTraceTest"
-
-#include <nativehelper/JNIHelp.h>
-#include <utils/Log.h>
-
-#include "jni.h"
-#include "perfetto/public/abi/data_source_abi.h"
-#include "perfetto/public/abi/heap_buffer.h"
-#include "perfetto/public/abi/pb_decoder_abi.h"
-#include "perfetto/public/abi/tracing_session_abi.h"
-#include "perfetto/public/abi/track_event_abi.h"
-#include "perfetto/public/compiler.h"
-#include "perfetto/public/data_source.h"
-#include "perfetto/public/pb_decoder.h"
-#include "perfetto/public/producer.h"
-#include "perfetto/public/protos/config/trace_config.pzc.h"
-#include "perfetto/public/protos/trace/interned_data/interned_data.pzc.h"
-#include "perfetto/public/protos/trace/test_event.pzc.h"
-#include "perfetto/public/protos/trace/trace.pzc.h"
-#include "perfetto/public/protos/trace/trace_packet.pzc.h"
-#include "perfetto/public/protos/trace/track_event/debug_annotation.pzc.h"
-#include "perfetto/public/protos/trace/track_event/track_descriptor.pzc.h"
-#include "perfetto/public/protos/trace/track_event/track_event.pzc.h"
-#include "perfetto/public/protos/trace/trigger.pzc.h"
-#include "perfetto/public/te_category_macros.h"
-#include "perfetto/public/te_macros.h"
-#include "perfetto/public/track_event.h"
-#include "protos/perfetto/trace/interned_data/interned_data.pb.h"
-#include "protos/perfetto/trace/trace.pb.h"
-#include "protos/perfetto/trace/trace_packet.pb.h"
-#include "tracing_perfetto.h"
-#include "utils.h"
-
-namespace android {
-using ::perfetto::protos::EventCategory;
-using ::perfetto::protos::EventName;
-using ::perfetto::protos::FtraceEvent;
-using ::perfetto::protos::FtraceEventBundle;
-using ::perfetto::protos::InternedData;
-using ::perfetto::protos::Trace;
-using ::perfetto::protos::TracePacket;
-
-using ::perfetto::shlib::test_utils::TracingSession;
-
-struct TracingSessionHolder {
-    TracingSession tracing_session;
-};
-
-static void nativeRegisterPerfetto([[maybe_unused]] JNIEnv* env, jclass /* obj */) {
-    tracing_perfetto::registerWithPerfetto(false /* test */);
-}
-
-static jlong nativeStartTracing(JNIEnv* env, jclass /* obj */, jbyteArray configBytes) {
-    jsize length = env->GetArrayLength(configBytes);
-    std::vector<uint8_t> data;
-    data.reserve(length);
-    env->GetByteArrayRegion(configBytes, 0, length, reinterpret_cast<jbyte*>(data.data()));
-
-    TracingSession session = TracingSession::FromBytes(data.data(), length);
-    TracingSessionHolder* holder = new TracingSessionHolder(std::move(session));
-
-    return reinterpret_cast<long>(holder);
-}
-
-static jbyteArray nativeStopTracing([[maybe_unused]] JNIEnv* env, jclass /* obj */, jlong ptr) {
-    TracingSessionHolder* holder = reinterpret_cast<TracingSessionHolder*>(ptr);
-
-    // Stop
-    holder->tracing_session.FlushBlocking(5000);
-    holder->tracing_session.StopBlocking();
-
-    std::vector<uint8_t> data = holder->tracing_session.ReadBlocking();
-
-    delete holder;
-
-    jbyteArray bytes = env->NewByteArray(data.size());
-    env->SetByteArrayRegion(bytes, 0, data.size(), reinterpret_cast<jbyte*>(data.data()));
-    return bytes;
-}
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
-    JNIEnv* env;
-    const JNINativeMethod methodTable[] = {/* name, signature, funcPtr */
-                                           {"nativeStartTracing", "([B)J",
-                                            (void*)nativeStartTracing},
-                                           {"nativeStopTracing", "(J)[B", (void*)nativeStopTracing},
-                                           {"nativeRegisterPerfetto", "()V",
-                                            (void*)nativeRegisterPerfetto}};
-
-    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        return JNI_ERR;
-    }
-
-    jniRegisterNativeMethods(env, "android/os/PerfettoTraceTest", methodTable,
-                             sizeof(methodTable) / sizeof(JNINativeMethod));
-
-    return JNI_VERSION_1_6;
-}
-
-} /* namespace android */
diff --git a/core/tests/coretests/src/android/app/NotificationManagerTest.java b/core/tests/coretests/src/android/app/NotificationManagerTest.java
index 4143229..9b97c8f 100644
--- a/core/tests/coretests/src/android/app/NotificationManagerTest.java
+++ b/core/tests/coretests/src/android/app/NotificationManagerTest.java
@@ -19,7 +19,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 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.atLeast;
@@ -304,9 +303,8 @@
 
         // It doesn't matter what the returned contents are, as long as we return a channel.
         // This setup must set up getNotificationChannels(), as that's the method called.
-        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
-                anyInt(), anyBoolean())).thenReturn(
-                    new ParceledListSlice<>(List.of(exampleChannel())));
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+                anyInt())).thenReturn(new ParceledListSlice<>(List.of(exampleChannel())));
 
         // ask for the same channel 100 times without invalidating the cache
         for (int i = 0; i < 100; i++) {
@@ -318,7 +316,7 @@
         NotificationChannel unused = mNotificationManager.getNotificationChannel("id");
 
         verify(mNotificationManager.mBackendService, times(2))
-                .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
+                .getNotificationChannels(any(), any(), anyInt());
     }
 
     @Test
@@ -331,24 +329,23 @@
         NotificationChannel c2 = new NotificationChannel("id2", "name2",
                 NotificationManager.IMPORTANCE_NONE);
 
-        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
-                anyInt(), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(c1, c2)));
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+                anyInt())).thenReturn(new ParceledListSlice<>(List.of(c1, c2)));
 
         assertThat(mNotificationManager.getNotificationChannel("id1")).isEqualTo(c1);
         assertThat(mNotificationManager.getNotificationChannel("id2")).isEqualTo(c2);
         assertThat(mNotificationManager.getNotificationChannel("id3")).isNull();
 
         verify(mNotificationManager.mBackendService, times(1))
-                .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
+                .getNotificationChannels(any(), any(), anyInt());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
     public void getNotificationChannels_cachedUntilInvalidated() throws Exception {
         NotificationManager.invalidateNotificationChannelCache();
-        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
-                anyInt(), anyBoolean())).thenReturn(
-                    new ParceledListSlice<>(List.of(exampleChannel())));
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+                anyInt())).thenReturn(new ParceledListSlice<>(List.of(exampleChannel())));
 
         // ask for channels 100 times without invalidating the cache
         for (int i = 0; i < 100; i++) {
@@ -360,7 +357,7 @@
         List<NotificationChannel> res = mNotificationManager.getNotificationChannels();
 
         verify(mNotificationManager.mBackendService, times(2))
-                .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
+                .getNotificationChannels(any(), any(), anyInt());
         assertThat(res).containsExactlyElementsIn(List.of(exampleChannel()));
     }
 
@@ -378,9 +375,8 @@
         NotificationChannel c2 = new NotificationChannel("other", "name2",
                 NotificationManager.IMPORTANCE_DEFAULT);
 
-        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
-                anyInt(), anyBoolean())).thenReturn(
-                    new ParceledListSlice<>(List.of(c1, conv1, c2)));
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+                anyInt())).thenReturn(new ParceledListSlice<>(List.of(c1, conv1, c2)));
 
         // Lookup for channel c1 and c2: returned as expected
         assertThat(mNotificationManager.getNotificationChannel("id")).isEqualTo(c1);
@@ -397,9 +393,9 @@
         // Lookup of a nonexistent channel is null
         assertThat(mNotificationManager.getNotificationChannel("id3")).isNull();
 
-        // All of that should have been one call to getOrCreateNotificationChannels()
+        // All of that should have been one call to getNotificationChannels()
         verify(mNotificationManager.mBackendService, times(1))
-                .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
+                .getNotificationChannels(any(), any(), anyInt());
     }
 
     @Test
@@ -419,12 +415,12 @@
         NotificationChannel channel3 = channel1.copy();
         channel3.setName("name3");
 
-        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg1),
-                eq(userId), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel1)));
-        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg2),
-                eq(userId), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel2)));
-        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg1),
-                eq(userId1), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel3)));
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg1),
+                eq(userId))).thenReturn(new ParceledListSlice<>(List.of(channel1)));
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg2),
+                eq(userId))).thenReturn(new ParceledListSlice<>(List.of(channel2)));
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg1),
+                eq(userId1))).thenReturn(new ParceledListSlice<>(List.of(channel3)));
 
         // set our context to pretend to be from package 1 and userId 0
         mContext.setParameters(pkg1, pkg1, userId);
@@ -440,7 +436,7 @@
 
         // Those should have been three different calls
         verify(mNotificationManager.mBackendService, times(3))
-                .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
+                .getNotificationChannels(any(), any(), anyInt());
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 5da2564..ac78e87 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -37,8 +37,10 @@
 import android.app.PropertyInvalidatedCache.NonceWatcher;
 import android.app.PropertyInvalidatedCache.NonceStore;
 import android.os.Binder;
+import android.util.Log;
 import com.android.internal.os.ApplicationSharedMemory;
 
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
@@ -410,8 +412,7 @@
 
     // Verify that invalidating the cache from an app process would fail due to lack of permissions.
     @Test
-    @android.platform.test.annotations.DisabledOnRavenwood(
-            reason = "SystemProperties doesn't have permission check")
+    @DisabledOnRavenwood(reason = "SystemProperties doesn't have permission check")
     public void testPermissionFailure() {
         // Create a cache that will write a system nonce.
         TestCache sysCache = new TestCache(MODULE_SYSTEM, "mode1");
@@ -556,8 +557,7 @@
     // storing nonces in shared memory.
     @RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED)
     @Test
-    @android.platform.test.annotations.DisabledOnRavenwood(
-            reason = "PIC doesn't use SharedMemory on Ravenwood")
+    @DisabledOnRavenwood(reason = "PIC doesn't use SharedMemory on Ravenwood")
     public void testSharedMemoryStorage() {
         // Fetch a shared memory instance for testing.
         ApplicationSharedMemory shmem = ApplicationSharedMemory.create();
@@ -602,6 +602,49 @@
         shmem.close();
     }
 
+    // Verify that the configured number of nonce slots is actually available.  This test
+    // hard-codes the configured number of slots, which means that this test must be changed
+    // whenever the shared memory configuration changes.
+    @RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED)
+    @Test
+    @DisabledOnRavenwood(reason = "PIC doesn't use SharedMemory on Ravenwood")
+    public void testSharedMemoryNonceConfig() {
+        // The two configured constants.  These are private to this method since they are only
+        // used here.
+        // LINT.IfChange(system_nonce_config)
+        final int maxNonce = 128;
+        final int maxByte = 8192;
+        // LINT.ThenChange(/core/jni/android_app_PropertyInvalidatedCache.h:system_nonce_config)
+
+        // Fetch a shared memory instance for testing.
+        ApplicationSharedMemory shmem = ApplicationSharedMemory.create();
+
+        // Create a server-side store.
+        NonceStore server = new NonceStore(shmem.getSystemNonceBlock(), true);
+
+        // Verify that the configured limits are as expected.
+        assertEquals(server.mMaxNonce, maxNonce);
+        assertEquals(server.mMaxByte, maxByte);
+
+        // Create mMaxNonce nonces.  These all succeed.
+        for (int i = 0; i < server.mMaxNonce; i++) {
+            String name = String.format("name_%03d", i);
+            assertEquals(i, server.storeName(name));
+        }
+
+        // Verify that we cannot create a nonce over the limit.
+        try {
+          int i = server.mMaxNonce;
+          String name = String.format("name_%03d", i);
+          server.storeName(name);
+          fail("expected a RuntimeException");
+        } catch (RuntimeException e) {
+          // Okay
+        }
+
+        shmem.close();
+    }
+
     // Verify that an invalid module causes an exception.
     private void testInvalidModule(String module) {
         try {
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index f9d449c..4ad6708 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -191,7 +191,7 @@
             .fuzzFractions()
             .mapNotNull{ FontScaleConverterFactory.forScale(it) }
             .flatMap{ table ->
-                generateSequenceOfFractions(-2000f..2000f, step = 0.1f)
+                generateSequenceOfFractions(-20f..100f, step = 0.1f)
                     .fuzzFractions()
                     .map{ Pair(table, it) }
             }
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
index 6d6b567..286c1b9 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
+++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
@@ -23,6 +23,7 @@
 import android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP
 import android.hardware.display.DisplayTopology.TreeNode.POSITION_RIGHT
 import android.util.SparseArray
+import android.util.SparseIntArray
 import android.view.Display
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
@@ -811,6 +812,13 @@
 
     @Test
     fun coordinates() {
+        // 1122222244
+        // 1122222244
+        // 11      44
+        // 11      44
+        // 1133333344
+        // 1133333344
+
         val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
             /* height= */ 600f, /* position= */ 0, /* offset= */ 0f)
 
@@ -837,6 +845,243 @@
         assertThat(coords.contentEquals(expectedCoords)).isTrue()
     }
 
+    @Test
+    fun graph() {
+        // 1122222244
+        // 1122222244
+        // 11      44
+        // 11      44
+        // 1133333344
+        // 1133333344
+        //        555
+        //        555
+        //        555
+
+        val densityPerDisplay = SparseIntArray()
+
+        val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+            /* height= */ 600f, /* position= */ 0, /* offset= */ 0f)
+        val density1 = 100
+        densityPerDisplay.append(1, density1)
+
+        val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
+        display1.addChild(display2)
+        val density2 = 200
+        densityPerDisplay.append(2, density2)
+
+        val primaryDisplayId = 3
+        val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
+        display1.addChild(display3)
+        val density3 = 150
+        densityPerDisplay.append(3, density3)
+
+        val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+            /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+        display2.addChild(display4)
+        val density4 = 300
+        densityPerDisplay.append(4, density4)
+
+        val display5 = DisplayTopology.TreeNode(/* displayId= */ 5, /* width= */ 300f,
+            /* height= */ 300f, POSITION_BOTTOM, /* offset= */ -100f)
+        display4.addChild(display5)
+        val density5 = 300
+        densityPerDisplay.append(5, density5)
+
+        topology = DisplayTopology(display1, primaryDisplayId)
+        val graph = topology.getGraph(densityPerDisplay)!!
+        val nodes = graph.displayNodes
+
+        assertThat(graph.primaryDisplayId).isEqualTo(primaryDisplayId)
+        assertThat(nodes.map {it.displayId}).containsExactly(1, 2, 3, 4, 5)
+        for (node in nodes) {
+            assertThat(node.density).isEqualTo(densityPerDisplay.get(node.displayId))
+            when (node.displayId) {
+                1 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 2, POSITION_RIGHT,
+                        /* offsetDp= */ 0f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_RIGHT,
+                        /* offsetDp= */ 400f))
+                2 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 1, POSITION_LEFT,
+                        /* offsetDp= */ 0f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 4, POSITION_RIGHT,
+                        /* offsetDp= */ 0f))
+                3 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 1, POSITION_LEFT,
+                        /* offsetDp= */ -400f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 4, POSITION_RIGHT,
+                        /* offsetDp= */ -400f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 5, POSITION_BOTTOM,
+                        /* offsetDp= */ 500f))
+                4 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 2, POSITION_LEFT,
+                        /* offsetDp= */ 0f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_LEFT,
+                        /* offsetDp= */ 400f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 5, POSITION_BOTTOM,
+                        /* offsetDp= */ -100f))
+                5 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_TOP,
+                        /* offsetDp= */ -500f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 4, POSITION_TOP,
+                        /* offsetDp= */ 100f))
+            }
+        }
+    }
+
+    @Test
+    fun graph_corner() {
+        // 1122244
+        // 1122244
+        // 1122244
+        //   333
+        // 55
+
+        val densityPerDisplay = SparseIntArray()
+
+        val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+            /* height= */ 300f, /* position= */ 0, /* offset= */ 0f)
+        val density1 = 100
+        densityPerDisplay.append(1, density1)
+
+        val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 300f,
+            /* height= */ 300f, POSITION_RIGHT, /* offset= */ 0f)
+        display1.addChild(display2)
+        val density2 = 200
+        densityPerDisplay.append(2, density2)
+
+        val primaryDisplayId = 3
+        val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 300f,
+            /* height= */ 100f, POSITION_BOTTOM, /* offset= */ 0f)
+        display2.addChild(display3)
+        val density3 = 150
+        densityPerDisplay.append(3, density3)
+
+        val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+            /* height= */ 300f, POSITION_RIGHT, /* offset= */ 0f)
+        display2.addChild(display4)
+        val density4 = 300
+        densityPerDisplay.append(4, density4)
+
+        val display5 = DisplayTopology.TreeNode(/* displayId= */ 5, /* width= */ 200f,
+            /* height= */ 100f, POSITION_BOTTOM, /* offset= */ -200f)
+        display3.addChild(display5)
+        val density5 = 300
+        densityPerDisplay.append(5, density5)
+
+        topology = DisplayTopology(display1, primaryDisplayId)
+        val graph = topology.getGraph(densityPerDisplay)!!
+        val nodes = graph.displayNodes
+
+        assertThat(graph.primaryDisplayId).isEqualTo(primaryDisplayId)
+        assertThat(nodes.map {it.displayId}).containsExactly(1, 2, 3, 4, 5)
+        for (node in nodes) {
+            assertThat(node.density).isEqualTo(densityPerDisplay.get(node.displayId))
+            when (node.displayId) {
+                1 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 2, POSITION_RIGHT,
+                        /* offsetDp= */ 0f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_RIGHT,
+                        /* offsetDp= */ 300f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_BOTTOM,
+                        /* offsetDp= */ 200f))
+                2 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 1, POSITION_LEFT,
+                        /* offsetDp= */ 0f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_BOTTOM,
+                        /* offsetDp= */ 0f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 4, POSITION_RIGHT,
+                        /* offsetDp= */ 0f))
+                3 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 1, POSITION_LEFT,
+                        /* offsetDp= */ -300f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 1, POSITION_TOP,
+                        /* offsetDp= */ -200f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 2, POSITION_TOP,
+                        /* offsetDp= */ 0f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 4, POSITION_RIGHT,
+                        /* offsetDp= */ -300f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 4, POSITION_TOP,
+                        /* offsetDp= */ 300f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 5, POSITION_LEFT,
+                        /* offsetDp= */ 100f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 5, POSITION_BOTTOM,
+                        /* offsetDp= */ -200f))
+                4 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 2, POSITION_LEFT,
+                        /* offsetDp= */ 0f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_LEFT,
+                        /* offsetDp= */ 300f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_BOTTOM,
+                        /* offsetDp= */ -300f))
+                5 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_TOP,
+                        /* offsetDp= */ 200f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_RIGHT,
+                        /* offsetDp= */ -100f))
+            }
+        }
+    }
+
+    @Test
+    fun graph_smallGap() {
+        // 11122
+        // 11122
+        // 11133
+        // 11133
+
+        // There is a gap between displays 2 and 3, small enough for them to still be adjacent.
+
+        val densityPerDisplay = SparseIntArray()
+
+        val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 300f,
+            /* height= */ 400f, /* position= */ 0, /* offset= */ 0f)
+        val density1 = 100
+        densityPerDisplay.append(1, density1)
+
+        val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ -1f)
+        display1.addChild(display2)
+        val density2 = 200
+        densityPerDisplay.append(2, density2)
+
+        val primaryDisplayId = 3
+        val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 200f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 201f)
+        display1.addChild(display3)
+        val density3 = 150
+        densityPerDisplay.append(3, density3)
+
+        topology = DisplayTopology(display1, primaryDisplayId)
+        val graph = topology.getGraph(densityPerDisplay)!!
+        val nodes = graph.displayNodes
+
+        assertThat(graph.primaryDisplayId).isEqualTo(primaryDisplayId)
+        assertThat(nodes.map {it.displayId}).containsExactly(1, 2, 3)
+        for (node in nodes) {
+            assertThat(node.density).isEqualTo(densityPerDisplay.get(node.displayId))
+            when (node.displayId) {
+                1 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 2, POSITION_RIGHT,
+                        /* offsetDp= */ -1f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_RIGHT,
+                        /* offsetDp= */ 201f))
+                2 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 1, POSITION_LEFT,
+                        /* offsetDp= */ 1f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 3, POSITION_BOTTOM,
+                        /* offsetDp= */ 0f))
+                3 -> assertThat(node.adjacentDisplays.toSet()).containsExactly(
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 1, POSITION_LEFT,
+                        /* offsetDp= */ -201f),
+                    DisplayTopologyGraph.AdjacentDisplay(/* displayId= */ 2, POSITION_TOP,
+                        /* offsetDp= */ 0f))
+            }
+        }
+    }
+
     /**
      * Runs the rearrange algorithm and returns the resulting tree as a list of nodes, with the
      * root at index 0. The number of nodes is inferred from the number of positions passed.
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index 31e0752..9aac02d 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -76,7 +76,7 @@
     /**
      * Create a test bundle, parcel it and return the parcel.
      */
-    private Parcel createBundleParcel(boolean withFd) throws Exception {
+    private Parcel createBundleParcel(boolean withFd, boolean hasIntent) throws Exception {
         final Bundle source = new Bundle();
         source.putString("string", "abc");
         source.putInt("int", 1);
@@ -85,13 +85,14 @@
             pipe[1].close();
             source.putParcelable("fd", pipe[0]);
         }
+        source.setHasIntent(hasIntent);
         return getParcelledBundle(source);
     }
 
     /**
      * Verify a bundle generated by {@link #createBundleParcel(boolean)}.
      */
-    private void checkBundle(Bundle b, boolean withFd) {
+    private void checkBundle(Bundle b, boolean withFd, boolean hasIntent) {
         // First, do the checks without actually unparceling the bundle.
         // (Note looking into the contents will unparcel a bundle, so we'll do it later.)
         assertTrue("mParcelledData shouldn't be null here.", b.isParcelled());
@@ -107,6 +108,8 @@
                     b.mFlags & (Bundle.FLAG_HAS_FDS | Bundle.FLAG_HAS_FDS_KNOWN));
         }
 
+        assertEquals(b.hasIntent(), hasIntent);
+
         // Then, check the contents.
         assertEquals("abc", b.getString("string"));
         assertEquals(1, b.getInt("int"));
@@ -139,42 +142,56 @@
         withFd = false;
 
         // new Bundle with p
-        p = createBundleParcel(withFd);
-        checkBundle(new Bundle(p), withFd);
+        p = createBundleParcel(withFd, false);
+        checkBundle(new Bundle(p), withFd, false);
         p.recycle();
 
         // new Bundle with p and length
-        p = createBundleParcel(withFd);
+        p = createBundleParcel(withFd, false);
         length = p.readInt();
-        checkBundle(new Bundle(p, length), withFd);
+        checkBundle(new Bundle(p, length), withFd, false);
         p.recycle();
 
         // readFromParcel()
-        p = createBundleParcel(withFd);
+        p = createBundleParcel(withFd, false);
         b = new Bundle();
         b.readFromParcel(p);
-        checkBundle(b, withFd);
+        checkBundle(b, withFd, false);
+        p.recycle();
+
+        // readFromParcel()
+        p = createBundleParcel(withFd, true);
+        b = new Bundle();
+        b.readFromParcel(p);
+        checkBundle(b, withFd, true);
         p.recycle();
 
         // Same test with FDs.
         withFd = true;
 
         // new Bundle with p
-        p = createBundleParcel(withFd);
-        checkBundle(new Bundle(p), withFd);
+        p = createBundleParcel(withFd, false);
+        checkBundle(new Bundle(p), withFd, false);
         p.recycle();
 
         // new Bundle with p and length
-        p = createBundleParcel(withFd);
+        p = createBundleParcel(withFd, false);
         length = p.readInt();
-        checkBundle(new Bundle(p, length), withFd);
+        checkBundle(new Bundle(p, length), withFd, false);
         p.recycle();
 
         // readFromParcel()
-        p = createBundleParcel(withFd);
+        p = createBundleParcel(withFd, false);
         b = new Bundle();
         b.readFromParcel(p);
-        checkBundle(b, withFd);
+        checkBundle(b, withFd, false);
+        p.recycle();
+
+        // readFromParcel()
+        p = createBundleParcel(withFd, true);
+        b = new Bundle();
+        b.readFromParcel(p);
+        checkBundle(b, withFd, true);
         p.recycle();
     }
 
@@ -486,6 +503,7 @@
         p.writeInt(131313); // Invalid type
         p.writeInt(0); // Anything, really
         int end = p.dataPosition();
+        p.writeBoolean(false);
         p.setDataPosition(0);
         return new Bundle(p, end - start);
     }
diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
index ad28383..0b5a446 100644
--- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -28,7 +28,6 @@
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.ArraySet;
-import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -84,19 +83,9 @@
     private final Set<String> mDebugAnnotationNames = new ArraySet<>();
     private final Set<String> mTrackNames = new ArraySet<>();
 
-    static {
-        try {
-            System.loadLibrary("perfetto_trace_test_jni");
-            Log.i(TAG, "Successfully loaded trace_test native library");
-        } catch (UnsatisfiedLinkError ule) {
-            Log.w(TAG, "Could not load trace_test native library");
-        }
-    }
-
     @Before
     public void setUp() {
-        PerfettoTrace.register();
-        nativeRegisterPerfetto();
+        PerfettoTrace.register(true);
         FOO_CATEGORY.register();
 
         mCategoryNames.clear();
@@ -110,7 +99,7 @@
     public void testDebugAnnotations() throws Exception {
         TraceConfig traceConfig = getTraceConfig(FOO);
 
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.instant(FOO_CATEGORY, "event")
                 .addFlow(2)
@@ -121,7 +110,7 @@
                 .addArg("string_val", FOO)
                 .emit();
 
-        byte[] traceBytes = nativeStopTracing(ptr);
+        byte[] traceBytes = session.close();
 
         Trace trace = Trace.parseFrom(traceBytes);
 
@@ -165,11 +154,11 @@
     public void testDebugAnnotationsWithLambda() throws Exception {
         TraceConfig traceConfig = getTraceConfig(FOO);
 
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.instant(FOO_CATEGORY, "event").addArg("long_val", 123L).emit();
 
-        byte[] traceBytes = nativeStopTracing(ptr);
+        byte[] traceBytes = session.close();
 
         Trace trace = Trace.parseFrom(traceBytes);
 
@@ -200,7 +189,7 @@
     public void testNamedTrack() throws Exception {
         TraceConfig traceConfig = getTraceConfig(FOO);
 
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.begin(FOO_CATEGORY, "event")
                 .usingNamedTrack(PerfettoTrace.getProcessTrackUuid(), FOO)
@@ -211,7 +200,7 @@
                 .usingNamedTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()), "bar")
                 .emit();
 
-        Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+        Trace trace = Trace.parseFrom(session.close());
 
         boolean hasTrackEvent = false;
         boolean hasTrackUuid = false;
@@ -248,7 +237,7 @@
     public void testProcessThreadNamedTrack() throws Exception {
         TraceConfig traceConfig = getTraceConfig(FOO);
 
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.begin(FOO_CATEGORY, "event")
                 .usingProcessNamedTrack(FOO)
@@ -259,7 +248,7 @@
                 .usingThreadNamedTrack(Process.myTid(), "%s-%s", "bar", "stool")
                 .emit();
 
-        Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+        Trace trace = Trace.parseFrom(session.close());
 
         boolean hasTrackEvent = false;
         boolean hasTrackUuid = false;
@@ -296,13 +285,13 @@
     public void testCounterSimple() throws Exception {
         TraceConfig traceConfig = getTraceConfig(FOO);
 
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.counter(FOO_CATEGORY, 16, FOO).emit();
 
         PerfettoTrace.counter(FOO_CATEGORY, 3.14, "bar").emit();
 
-        Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+        Trace trace = Trace.parseFrom(session.close());
 
         boolean hasTrackEvent = false;
         boolean hasCounterValue = false;
@@ -339,7 +328,7 @@
     public void testCounter() throws Exception {
         TraceConfig traceConfig = getTraceConfig(FOO);
 
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.counter(FOO_CATEGORY, 16)
                 .usingCounterTrack(PerfettoTrace.getProcessTrackUuid(), FOO).emit();
@@ -348,7 +337,7 @@
                 .usingCounterTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()),
                                    "%s-%s", "bar", "stool").emit();
 
-        Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+        Trace trace = Trace.parseFrom(session.close());
 
         boolean hasTrackEvent = false;
         boolean hasCounterValue = false;
@@ -385,14 +374,14 @@
     public void testProcessThreadCounter() throws Exception {
         TraceConfig traceConfig = getTraceConfig(FOO);
 
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.counter(FOO_CATEGORY, 16).usingProcessCounterTrack(FOO).emit();
 
         PerfettoTrace.counter(FOO_CATEGORY, 3.14)
                 .usingThreadCounterTrack(Process.myTid(), "%s-%s", "bar", "stool").emit();
 
-        Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+        Trace trace = Trace.parseFrom(session.close());
 
         boolean hasTrackEvent = false;
         boolean hasCounterValue = false;
@@ -429,7 +418,7 @@
     public void testProto() throws Exception {
         TraceConfig traceConfig = getTraceConfig(FOO);
 
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.instant(FOO_CATEGORY, "event_proto")
                 .beginProto()
@@ -441,7 +430,7 @@
                 .endProto()
                 .emit();
 
-        byte[] traceBytes = nativeStopTracing(ptr);
+        byte[] traceBytes = session.close();
 
         Trace trace = Trace.parseFrom(traceBytes);
 
@@ -477,7 +466,7 @@
     public void testProtoNested() throws Exception {
         TraceConfig traceConfig = getTraceConfig(FOO);
 
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.instant(FOO_CATEGORY, "event_proto_nested")
                 .beginProto()
@@ -494,7 +483,7 @@
                 .endProto()
                 .emit();
 
-        byte[] traceBytes = nativeStopTracing(ptr);
+        byte[] traceBytes = session.close();
 
         Trace trace = Trace.parseFrom(traceBytes);
 
@@ -538,13 +527,13 @@
     public void testActivateTrigger() throws Exception {
         TraceConfig traceConfig = getTriggerTraceConfig(FOO, FOO);
 
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.instant(FOO_CATEGORY, "event_trigger").emit();
 
         PerfettoTrace.activateTrigger(FOO, 1000);
 
-        byte[] traceBytes = nativeStopTracing(ptr);
+        byte[] traceBytes = session.close();
 
         Trace trace = Trace.parseFrom(traceBytes);
 
@@ -569,7 +558,7 @@
         TraceConfig traceConfig = getTraceConfig(BAR);
 
         Category barCategory = new Category(BAR);
-        long ptr = nativeStartTracing(traceConfig.toByteArray());
+        PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
 
         PerfettoTrace.instant(barCategory, "event")
                 .addArg("before", 1)
@@ -581,7 +570,7 @@
                 .addArg("after", 1)
                 .emit();
 
-        byte[] traceBytes = nativeStopTracing(ptr);
+        byte[] traceBytes = session.close();
 
         Trace trace = Trace.parseFrom(traceBytes);
 
@@ -603,10 +592,6 @@
         assertThat(mDebugAnnotationNames).doesNotContain("before");
     }
 
-    private static native long nativeStartTracing(byte[] config);
-    private static native void nativeRegisterPerfetto();
-    private static native byte[] nativeStopTracing(long ptr);
-
     private TrackEvent getTrackEvent(Trace trace, int idx) {
         int curIdx = 0;
         for (TracePacket packet: trace.getPacketList()) {
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
index 1bdb006..9f1580c 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -124,7 +124,8 @@
                     getRankingAdjustment(i),
                     isBubble(i),
                     getProposedImportance(i),
-                    hasSensitiveContent(i)
+                    hasSensitiveContent(i),
+                    getSummarization(i)
             );
             rankings[i] = ranking;
         }
@@ -334,6 +335,17 @@
     }
 
     /**
+     * Produces a String that can be used to represent getSummarization, based on the provided
+     * index.
+     */
+    public static String getSummarization(int index) {
+        if ((android.app.Flags.nmSummarizationUi() || android.app.Flags.nmSummarization())) {
+            return "summary " + index;
+        }
+        return null;
+    }
+
+    /**
      * Produces a boolean that can be used to represent isBubble, based on the provided index.
      */
     public static boolean isBubble(int index) {
@@ -461,7 +473,8 @@
                 /* rankingAdjustment= */ 0,
                 /* isBubble= */ false,
                 /* proposedImportance= */ 0,
-                /* sensitiveContent= */ false
+                /* sensitiveContent= */ false,
+                /* summarization = */ null
         );
         return ranking;
     }
@@ -550,7 +563,8 @@
                 tweak.getRankingAdjustment(),
                 tweak.isBubble(),
                 tweak.getProposedImportance(),
-                tweak.hasSensitiveContent()
+                tweak.hasSensitiveContent(),
+                tweak.getSummarization()
         );
         assertNotEquals(nru, nru2);
     }
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index cef6970..c40137f 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -1054,8 +1054,7 @@
         ViewRootImpl viewRootImpl = mView.getViewRootImpl();
         sInstrumentation.runOnMainSync(() -> {
             mView.invalidate();
-            viewRootImpl.notifyInsetsAnimationRunningStateChanged(true, 0 /* animationType */,
-                    0 /* insetsTypes */  /* areOtherAnimationsRunning */);
+            viewRootImpl.notifyInsetsAnimationRunningStateChanged(true);
             mView.invalidate();
         });
         sInstrumentation.waitForIdleSync();
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index ee8d428..f87b699 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -224,7 +224,7 @@
         }
 
         @Override
-        void internalFlush(int reason) {
+        void flush(int reason) {
             throw new UnsupportedOperationException("should not have been called");
         }
 
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index a1d7f87..b42bcee 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -263,7 +263,7 @@
         session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
         session.mDirectServiceInterface = mMockContentCaptureDirectManager;
 
-        session.internalFlush(REASON);
+        session.flush(REASON);
         mTestableLooper.processAllMessages();
 
         verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -280,7 +280,7 @@
         session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
         session.mDirectServiceInterface = mMockContentCaptureDirectManager;
 
-        session.internalFlush(REASON);
+        session.flush(REASON);
         mTestableLooper.processAllMessages();
 
         verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -298,7 +298,7 @@
         session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
         session.mDirectServiceInterface = mMockContentCaptureDirectManager;
 
-        session.internalFlush(REASON);
+        session.flush(REASON);
         mTestableLooper.processAllMessages();
 
         verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -316,7 +316,7 @@
         session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
         session.mDirectServiceInterface = mMockContentCaptureDirectManager;
 
-        session.internalFlush(REASON);
+        session.flush(REASON);
         mTestableLooper.processAllMessages();
 
         verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -499,57 +499,6 @@
         assertThat(session.mEventProcessQueue).hasSize(1);
     }
 
-    @Test
-    public void notifyContentCaptureEvents_beforeSessionPerformStart() throws RemoteException {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ true);
-        MainContentCaptureSession session = createSession(options);
-        session.mContentCaptureHandler = null;
-        session.mDirectServiceInterface = null;
-
-        notifyContentCaptureEvents(session);
-        mTestableLooper.processAllMessages();
-
-        assertThat(session.mEvents).isNull();
-        assertThat(session.mEventProcessQueue).hasSize(7); // 5 view events + 2 view tree events
-    }
-
-    @Test
-    public void notifyViewAppeared_beforeSessionPerformStart() throws RemoteException {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ true);
-        MainContentCaptureSession session = createSession(options);
-        session.mContentCaptureHandler = null;
-        session.mDirectServiceInterface = null;
-
-        View view = prepareView(session);
-        session.notifyViewAppeared(session.newViewStructure(view));
-
-        assertThat(session.mEvents).isNull();
-        assertThat(session.mEventProcessQueue).hasSize(1);
-    }
-
-    @Test
-    public void flush_beforeSessionPerformStart() throws Exception {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ true);
-        MainContentCaptureSession session = createSession(options);
-        session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
-        session.mContentCaptureHandler = null;
-        session.mDirectServiceInterface = null;
-
-        session.internalFlush(REASON);
-
-        assertThat(session.mEvents).hasSize(1);
-        assertThat(session.mEventProcessQueue).isEmpty();
-    }
-
     /** Simulates the regular content capture events sequence. */
     private void notifyContentCaptureEvents(final MainContentCaptureSession session) {
         final ArrayList<Object> events = new ArrayList<>(
@@ -612,8 +561,8 @@
                         sStrippedContext,
                         manager,
                         testHandler,
+                        testHandler,
                         mMockSystemServerInterface);
-        session.mContentCaptureHandler = testHandler;
         session.mComponentName = COMPONENT_NAME;
         return session;
     }
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 2880ecf..8b0d315 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -36,6 +36,7 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -934,6 +935,136 @@
         assertEquals(testText, replacedTextView.getText());
     }
 
+    @Test
+    public void estimateMemoryUsage() {
+        Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888);
+        int b1Memory = b1.getAllocationByteCount();
+        Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888);
+        int b2Memory = b2.getAllocationByteCount();
+        Bitmap b3 = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888);
+        int b3Memory = b3.getAllocationByteCount();
+
+        final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
+        assertEquals(0, rv.estimateMemoryUsage());
+        assertEquals(0, rv.estimateIconMemoryUsage());
+        assertEquals(0, rv.estimateTotalBitmapMemoryUsage());
+
+        rv.setBitmap(R.id.view, "", b1);
+        rv.setImageViewBitmap(R.id.view, b1); // second instance of b1 is cached
+        rv.setBitmap(R.id.view, "", b2);
+        assertEquals(b1Memory + b2Memory, rv.estimateMemoryUsage());
+        assertEquals(0, rv.estimateIconMemoryUsage());
+        assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage());
+
+        rv.setIcon(R.id.view, "", Icon.createWithBitmap(b2));
+        rv.setIcon(R.id.view, "", Icon.createWithBitmap(b3));
+        rv.setImageViewIcon(R.id.view, Icon.createWithBitmap(b3));
+        assertEquals(b1Memory + b2Memory, rv.estimateMemoryUsage());
+        assertEquals(b2Memory + (2 * b3Memory), rv.estimateIconMemoryUsage());
+        assertEquals(b1Memory + (2 * b2Memory) + (2 * b3Memory),
+                rv.estimateTotalBitmapMemoryUsage());
+    }
+
+    @Test
+    public void estimateMemoryUsage_landscapePortrait() {
+        Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888);
+        int b1Memory = b1.getAllocationByteCount();
+        Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888);
+        int b2Memory = b2.getAllocationByteCount();
+        Bitmap b3 = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888);
+        int b3Memory = b3.getAllocationByteCount();
+        Bitmap b4 = Bitmap.createBitmap(320, 240, Bitmap.Config.ARGB_8888);
+        int b4Memory = b4.getAllocationByteCount();
+
+        // Landscape and portrait using same bitmaps get counted twice.
+        final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
+        rv.setBitmap(R.id.view, "", b1);
+        rv.setIcon(R.id.view, "", Icon.createWithBitmap(b2));
+        RemoteViews landscapePortraitViews = new RemoteViews(rv, rv);
+        assertEquals(b1Memory, landscapePortraitViews.estimateMemoryUsage());
+        assertEquals(2 * b2Memory, landscapePortraitViews.estimateIconMemoryUsage());
+        assertEquals(b1Memory + (2 * b2Memory),
+                landscapePortraitViews.estimateTotalBitmapMemoryUsage());
+
+        final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test);
+        rv.setBitmap(R.id.view, "", b3);
+        rv.setIcon(R.id.view, "", Icon.createWithBitmap(b4));
+        landscapePortraitViews = new RemoteViews(rv, rv2);
+        assertEquals(b1Memory + b3Memory, landscapePortraitViews.estimateMemoryUsage());
+        assertEquals(b2Memory + b4Memory, landscapePortraitViews.estimateIconMemoryUsage());
+        assertEquals(b1Memory + b2Memory + b3Memory + b4Memory,
+                landscapePortraitViews.estimateTotalBitmapMemoryUsage());
+    }
+
+    @Test
+    public void estimateMemoryUsage_sizedViews() {
+        Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888);
+        int b1Memory = b1.getAllocationByteCount();
+        Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888);
+        int b2Memory = b2.getAllocationByteCount();
+        Bitmap b3 = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888);
+        int b3Memory = b3.getAllocationByteCount();
+        Bitmap b4 = Bitmap.createBitmap(320, 240, Bitmap.Config.ARGB_8888);
+        int b4Memory = b4.getAllocationByteCount();
+
+        // Sized views using same bitmaps do not get counted twice.
+        final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
+        rv.setBitmap(R.id.view, "", b1);
+        rv.setIcon(R.id.view, "", Icon.createWithBitmap(b2));
+        RemoteViews sizedViews = new RemoteViews(
+                Map.of(new SizeF(0f, 0f), rv, new SizeF(1f, 1f), rv));
+        assertEquals(b1Memory, sizedViews.estimateMemoryUsage());
+        assertEquals(2 * b2Memory, sizedViews.estimateIconMemoryUsage());
+        assertEquals(b1Memory + (2 * b2Memory), sizedViews.estimateTotalBitmapMemoryUsage());
+
+        final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test);
+        rv.setBitmap(R.id.view, "", b3);
+        rv.setIcon(R.id.view, "", Icon.createWithBitmap(b4));
+        sizedViews = new RemoteViews(Map.of(new SizeF(0f, 0f), rv, new SizeF(1f, 1f), rv2));
+        assertEquals(b1Memory + b3Memory, sizedViews.estimateMemoryUsage());
+        assertEquals(b2Memory + b4Memory, sizedViews.estimateIconMemoryUsage());
+        assertEquals(b1Memory + b2Memory + b3Memory + b4Memory,
+                sizedViews.estimateTotalBitmapMemoryUsage());
+    }
+
+    @Test
+    public void estimateMemoryUsage_nestedViews() {
+        Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888);
+        int b1Memory = b1.getAllocationByteCount();
+        Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888);
+        int b2Memory = b2.getAllocationByteCount();
+
+        final RemoteViews rv1 = new RemoteViews(mPackage, R.layout.remote_views_test);
+        rv1.setBitmap(R.id.view, "", b1);
+        final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test);
+        rv2.setIcon(R.id.view, "", Icon.createWithBitmap(b2));
+        final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
+        rv.addView(R.id.view, rv1);
+        rv.addView(R.id.view, rv2);
+        assertEquals(b1Memory, rv.estimateMemoryUsage());
+        assertEquals(b2Memory, rv.estimateIconMemoryUsage());
+        assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage());
+    }
+
+    @Test
+    public void estimateMemoryUsage_remoteCollectionItems() {
+        Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888);
+        int b1Memory = b1.getAllocationByteCount();
+        Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888);
+        int b2Memory = b2.getAllocationByteCount();
+
+        final RemoteViews rv1 = new RemoteViews(mPackage, R.layout.remote_views_test);
+        rv1.setBitmap(R.id.view, "", b1);
+        final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test);
+        rv2.setIcon(R.id.view, "", Icon.createWithBitmap(b2));
+        final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
+        rv.setRemoteAdapter(R.id.view, new RemoteViews.RemoteCollectionItems.Builder().addItem(0L,
+                rv1).addItem(1L, rv2).build());
+        assertEquals(b1Memory, rv.estimateMemoryUsage());
+        assertEquals(b2Memory, rv.estimateIconMemoryUsage());
+        assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage());
+    }
+
     private static LayoutInflater.Factory2 createLayoutInflaterFactory(String viewTypeToReplace,
             View replacementView) {
         return new LayoutInflater.Factory2() {
diff --git a/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java b/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java
new file mode 100644
index 0000000..cc06f3d
--- /dev/null
+++ b/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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 android.window;
+
+import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
+
+import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.support.test.uiautomator.UiDevice;
+import android.window.DesktopExperienceFlags.DesktopExperienceFlag;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.window.flags.Flags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ * Test class for {@link android.window.DesktopExperienceFlags}
+ *
+ * <p>Build/Install/Run: atest FrameworksCoreTests:DesktopExperienceFlagsTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(ParameterizedAndroidJunit4.class)
+public class DesktopExperienceFlagsTest {
+
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.allCombinationsOf(FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION);
+    }
+
+    @Rule public SetFlagsRule mSetFlagsRule;
+
+    private UiDevice mUiDevice;
+    private Context mContext;
+    private boolean mLocalFlagValue = false;
+    private final DesktopExperienceFlag mOverriddenLocalFlag =
+            new DesktopExperienceFlag(() -> mLocalFlagValue, true);
+    private final DesktopExperienceFlag mNotOverriddenLocalFlag =
+            new DesktopExperienceFlag(() -> mLocalFlagValue, false);
+
+    private static final String OVERRIDE_OFF_SETTING = "0";
+    private static final String OVERRIDE_ON_SETTING = "1";
+    private static final String OVERRIDE_INVALID_SETTING = "garbage";
+
+    public DesktopExperienceFlagsTest(FlagsParameterization flags) {
+        mSetFlagsRule = new SetFlagsRule(flags);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        setSysProp(null);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        resetCache();
+        setSysProp(null);
+    }
+
+    @Test
+    public void isTrue_overrideOff_featureFlagOn_returnsTrue() throws Exception {
+        mLocalFlagValue = true;
+        setSysProp(OVERRIDE_OFF_SETTING);
+
+        assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+        assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
+    }
+
+    @Test
+    public void isTrue_overrideOn_featureFlagOn_returnsTrue() throws Exception {
+        mLocalFlagValue = true;
+        setSysProp(OVERRIDE_ON_SETTING);
+
+        assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+        assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
+    }
+
+    @Test
+    public void isTrue_overrideOff_featureFlagOff_returnsFalse() throws Exception {
+        mLocalFlagValue = false;
+        setSysProp(OVERRIDE_OFF_SETTING);
+
+        assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+        assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
+    }
+
+    @Test
+    public void isTrue_devOptionEnabled_overrideOn_featureFlagOff() throws Exception {
+        assumeTrue(Flags.showDesktopExperienceDevOption());
+        mLocalFlagValue = false;
+        setSysProp(OVERRIDE_ON_SETTING);
+
+        assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+        assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
+    }
+
+    @Test
+    public void isTrue_devOptionDisabled_overrideOn_featureFlagOff_returnsFalse() throws Exception {
+        assumeFalse(Flags.showDesktopExperienceDevOption());
+        mLocalFlagValue = false;
+        setSysProp(OVERRIDE_ON_SETTING);
+
+        assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+        assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
+    }
+
+    private void setSysProp(String value) throws Exception {
+        if (value == null) {
+            resetSysProp();
+        } else {
+            mUiDevice.executeShellCommand(
+                    "setprop " + DesktopModeFlags.SYSTEM_PROPERTY_NAME + " " + value);
+        }
+    }
+
+    private void resetSysProp() throws Exception {
+        mUiDevice.executeShellCommand("setprop " + DesktopModeFlags.SYSTEM_PROPERTY_NAME + " ''");
+    }
+
+    private void resetCache() throws Exception {
+        Field cachedToggleOverride =
+                DesktopExperienceFlags.class.getDeclaredField("sCachedToggleOverride");
+        cachedToggleOverride.setAccessible(true);
+        cachedToggleOverride.set(null, null);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index ec19c0c..9baa31f 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -143,8 +143,8 @@
                 parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
                 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        // Colors with 40% opacity
-        int fadedRed = 0x66FF0000;
+        // Colors with 50% opacity
+        int fadedRed = 0x80FF0000;
         expectedDrawableParts = new ArrayList<>(
                 List.of(new DrawableSegment(0, 300, fadedRed, true)));
 
@@ -271,8 +271,8 @@
                 parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
                 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        // Colors with 40% opacity
-        int fadedBlue = 0x660000FF;
+        // Colors with 50% opacity
+        int fadedBlue = 0x800000FF;
         expectedDrawableParts = new ArrayList<>(
                 List.of(new DrawableSegment(0, 180, Color.BLUE),
                         new DrawableSegment(180, 300, fadedBlue, true)));
@@ -321,8 +321,8 @@
                 parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
                 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        // Colors with 40% opacity
-        int fadedGreen = 0x6600FF00;
+        // Colors with 50% opacity
+        int fadedGreen = 0x8000FF00;
         expectedDrawableParts = new ArrayList<>(List.of(new DrawableSegment(0, 146, Color.RED),
                 new DrawableSegment(150, 180, Color.GREEN),
                 new DrawableSegment(180, 300, fadedGreen, true)));
@@ -370,8 +370,8 @@
                 parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
                 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        // Colors with 40% opacity
-        int fadedGreen = 0x6600FF00;
+        // Colors with 50% opacity
+        int fadedGreen = 0x8000FF00;
         expectedDrawableParts = new ArrayList<>(List.of(new DrawableSegment(0, 146, Color.RED),
                 new DrawableSegment(150, 176, Color.GREEN),
                 new DrawableSegment(180, 300, fadedGreen, true)));
@@ -439,9 +439,9 @@
                 parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
                 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        // Colors with 40% opacity
-        int fadedBlue = 0x660000FF;
-        int fadedYellow = 0x66FFFF00;
+        // Colors with 50% opacity
+        int fadedBlue = 0x800000FF;
+        int fadedYellow = 0x80FFFF00;
         expectedDrawableParts = new ArrayList<>(
                 List.of(new DrawableSegment(0, 34.219177F, Color.BLUE),
                         new DrawablePoint(38.219177F, 50.219177F, Color.RED),
@@ -517,9 +517,9 @@
                 parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
                 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        // Colors with 40% opacity
-        int fadedGreen = 0x6600FF00;
-        int fadedYellow = 0x66FFFF00;
+        // Colors with 50% opacity
+        int fadedGreen = 0x8000FF00;
+        int fadedYellow = 0x80FFFF00;
         expectedDrawableParts = new ArrayList<>(
                 List.of(new DrawableSegment(0, 34.095238F, Color.RED),
                         new DrawablePoint(38.095238F, 50.095238F, Color.RED),
@@ -594,9 +594,9 @@
                 parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
                 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        // Colors with 40% opacity
-        int fadedGreen = 0x6600FF00;
-        int fadedYellow = 0x66FFFF00;
+        // Colors with 50% opacity
+        int fadedGreen = 0x8000FF00;
+        int fadedYellow = 0x80FFFF00;
         expectedDrawableParts = new ArrayList<>(
                 List.of(new DrawablePoint(0, 12, Color.RED),
                         new DrawableSegment(16, 65, Color.RED),
@@ -675,9 +675,9 @@
                 parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
                 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        // Colors with 40% opacity
-        int fadedGreen = 0x6600FF00;
-        int fadedYellow = 0x66FFFF00;
+        // Colors with 50% opacity
+        int fadedGreen = 0x8000FF00;
+        int fadedYellow = 0x80FFFF00;
         expectedDrawableParts = new ArrayList<>(
                 List.of(new DrawableSegment(0, 16, Color.RED),
                         new DrawablePoint(20, 32, Color.RED),
@@ -1039,9 +1039,9 @@
                         isStyledByProgress
                 );
 
-        // Colors with 40% opacity
-        int fadedBlue = 0x660000FF;
-        int fadedYellow = 0x66FFFF00;
+        // Colors with 50% opacity
+        int fadedBlue = 0x800000FF;
+        int fadedYellow = 0x80FFFF00;
         List<DrawablePart> expectedDrawableParts = new ArrayList<>(
                 List.of(new DrawableSegment(0, 34.219177F, Color.BLUE),
                         new DrawablePoint(38.219177F, 50.219177F, Color.RED),
@@ -1089,8 +1089,8 @@
                         isStyledByProgress
                 );
 
-        // Colors with 40% opacity
-        int fadedBlue = 0x660000FF;
+        // Colors with 50%f opacity
+        int fadedBlue = 0x800000FF;
         List<DrawablePart> expectedDrawableParts = new ArrayList<>(
                 List.of(new DrawableSegment(0, 60.000004F, Color.BLUE),
                         new DrawableSegment(60.000004F, 100, fadedBlue, true)));
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index b8059d0..1edbffa 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -594,7 +594,6 @@
         <!-- Permission required for CTS test - FileIntegrityManagerTest -->
         <permission name="android.permission.SETUP_FSVERITY" />
         <!-- Permissions required for CTS test - AppFunctionManagerTest -->
-        <permission name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" />
         <permission name="android.permission.EXECUTE_APP_FUNCTIONS" />
         <!-- Permission required for CTS test - CtsNfcTestCases -->
         <permission name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" />
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 0656446..13d0169 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -177,10 +177,3 @@
     description: "Factor task-view state tracking out of taskviewtransitions"
     bug: "384976265"
 }
-
-flag {
-    name: "enable_non_default_display_split"
-    namespace: "multitasking"
-    description: "Enables split screen on non default displays"
-    bug: "384999213"
-}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
index dd387b3..09a93d5 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
@@ -296,5 +296,9 @@
         override fun onBubbleStateChange(update: BubbleBarUpdate?) {}
 
         override fun animateBubbleBarLocation(location: BubbleBarLocation?) {}
+
+        override fun onDragItemOverBubbleBarDragZone(location: BubbleBarLocation) {}
+
+        override fun onItemDraggedOutsideBubbleBarDropZone() {}
     }
 }
diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_horizontal_arrow_tooltip.xml
similarity index 95%
rename from libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml
rename to libs/WindowManager/Shell/res/layout/desktop_windowing_education_horizontal_arrow_tooltip.xml
index fd75827..d159a27 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_horizontal_arrow_tooltip.xml
@@ -18,7 +18,8 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:elevation="1dp"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:gravity="start">
 
     <!-- ImageView for the arrow icon, positioned horizontally at the start of the tooltip
     container. -->
diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml
index debcba0..122cde0 100644
--- a/libs/WindowManager/Shell/res/values/ids.xml
+++ b/libs/WindowManager/Shell/res/values/ids.xml
@@ -46,4 +46,9 @@
     <item type="id" name="action_move_bubble_bar_right"/>
 
     <item type="id" name="dismiss_view"/>
+
+    <!-- Accessibility actions for desktop windowing. -->
+    <item type="id" name="action_snap_left"/>
+    <item type="id" name="action_snap_right"/>
+    <item type="id" name="action_maximize_restore"/>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 468c345..c29b927 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -333,6 +333,28 @@
     <!-- Accessibility text for the Maximize Menu's snap right button [CHAR LIMIT=NONE] -->
     <string name="desktop_mode_maximize_menu_snap_right_button_text">Snap right</string>
 
+    <!-- Accessibility text for the Maximize Menu's snap left button [CHAR LIMIT=NONE] -->
+    <string name="desktop_mode_a11y_action_snap_left">Resize app window left</string>
+    <!-- Accessibility text for the Maximize Menu's snap right button [CHAR LIMIT=NONE] -->
+    <string name="desktop_mode_a11y_action_snap_right">Resize app window right</string>
+    <!-- Accessibility text for the Maximize Menu's snap maximize/restore [CHAR LIMIT=NONE] -->
+    <string name="desktop_mode_a11y_action_maximize_restore">Maximize or restore window size</string>
+
+    <!-- Accessibility action replacement for caption handle menu split screen button [CHAR LIMIT=NONE] -->
+    <string name="app_handle_menu_talkback_split_screen_mode_button_text">Enter split screen mode</string>
+    <!-- Accessibility action replacement for caption handle menu enter desktop mode button [CHAR LIMIT=NONE] -->
+    <string name="app_handle_menu_talkback_desktop_mode_button_text">Enter desktop windowing mode</string>
+    <!-- Accessibility action replacement for maximize menu enter snap left button [CHAR LIMIT=NONE] -->
+    <string name="maximize_menu_talkback_action_snap_left_text">Resize window to left</string>
+    <!-- Accessibility action replacement for maximize menu enter snap right button [CHAR LIMIT=NONE] -->
+    <string name="maximize_menu_talkback_action_snap_right_text">Resize window to right</string>
+    <!-- Accessibility action replacement for maximize menu enter maximize/restore button [CHAR LIMIT=NONE] -->
+    <string name="maximize_menu_talkback_action_maximize_restore_text">Maximize or restore window size</string>
+    <!-- Accessibility action replacement for app header maximize/restore button [CHAR LIMIT=NONE] -->
+    <string name="maximize_button_talkback_action_maximize_restore_text">Maximize or restore window size</string>
+    <!-- Accessibility action replacement for app header minimize button [CHAR LIMIT=NONE] -->
+    <string name="minimize_button_talkback_action_maximize_restore_text">Minimize app window</string>
+
     <!-- Accessibility text for open by default settings button [CHAR LIMIT=NONE] -->
     <string name="open_by_default_settings_text">Open by default settings</string>
     <!-- Subheader for open by default menu string. -->
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
index 2ca011b..0a1e3b9 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
@@ -111,7 +111,7 @@
      * Create new for a pair of tasks in split screen
      */
     public static GroupedTaskInfo forSplitTasks(@NonNull TaskInfo task1,
-                    @NonNull TaskInfo task2, @Nullable SplitBounds splitBounds) {
+                    @NonNull TaskInfo task2, @NonNull SplitBounds splitBounds) {
         return new GroupedTaskInfo(List.of(task1, task2), splitBounds, TYPE_SPLIT,
                 null /* minimizedFreeformTasks */);
     }
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
index 840de2c..4d00c741 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
@@ -127,6 +127,46 @@
     }
 
     /**
+     * Check if all changes in this transition are only ordering changes. If so, we won't animate.
+     */
+    public static boolean isAllOrderOnly(TransitionInfo info) {
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            if (!isOrderOnly(info.getChanges().get(i))) return false;
+        }
+        return true;
+    }
+
+    /**
+     * Look through a transition and see if all non-closing changes are no-animation. If so, no
+     * animation should play.
+     */
+    public static boolean isAllNoAnimation(TransitionInfo info) {
+        if (isClosingType(info.getType())) {
+            // no-animation is only relevant for launching (open) activities.
+            return false;
+        }
+        boolean hasNoAnimation = false;
+        final int changeSize = info.getChanges().size();
+        for (int i = changeSize - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (isClosingType(change.getMode())) {
+                // ignore closing apps since they are a side-effect of the transition and don't
+                // animate.
+                continue;
+            }
+            if (change.hasFlags(TransitionInfo.FLAG_NO_ANIMATION)) {
+                hasNoAnimation = true;
+            } else if (!isOrderOnly(change) && !change.hasFlags(TransitionInfo.FLAG_IS_OCCLUDED)) {
+                // Ignore the order only or occluded changes since they shouldn't be visible during
+                // animation. For anything else, we need to animate if at-least one relevant
+                // participant *is* animated,
+                return false;
+            }
+        }
+        return hasNoAnimation;
+    }
+
+    /**
      * Filter that selects leaf-tasks only. THIS IS ORDER-DEPENDENT! For it to work properly, you
      * MUST call `test` in the same order that the changes appear in the TransitionInfo.
      */
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimator.kt
index 9d3b56d..812b358 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimator.kt
@@ -985,6 +985,11 @@
             return animators[target] as PhysicsAnimator<T>
         }
 
+        @JvmStatic
+        @Suppress("UNCHECKED_CAST")
+        fun <T: Any> getInstanceIfExists(target: T): PhysicsAnimator<T>? =
+            animators[target] as PhysicsAnimator<T>?
+
         /**
          * Set whether all physics animators should log a lot of information about animations.
          * Useful for debugging!
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt
index 91d66ea..d1c34a4 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt
@@ -22,6 +22,7 @@
 import android.graphics.Rect
 import android.util.DisplayMetrics
 import android.util.TypedValue
+import android.view.Choreographer
 import android.view.SurfaceControl
 import android.view.animation.Interpolator
 import android.window.TransitionInfo
@@ -82,6 +83,7 @@
                 transaction
                     .setPosition(leash, animPos.x, animPos.y)
                     .setScale(leash, animScale, animScale)
+                    .setFrameTimeline(Choreographer.getInstance().vsyncId)
                     .apply()
             }
         }
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
new file mode 100644
index 0000000..835456b
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.shared.desktopmode
+
+import android.app.TaskInfo
+import android.content.Context
+import android.window.DesktopModeFlags
+import com.android.internal.R
+import java.util.ArrayList
+
+/**
+ * Class to decide whether to apply app compat policies in desktop mode.
+ */
+// TODO(b/347289970): Consider replacing with API
+class DesktopModeCompatPolicy(private val context: Context) {
+
+    private val systemUiPackage: String = context.resources.getString(R.string.config_systemUi)
+    private val defaultHomePackage: String?
+        get() = context.getPackageManager().getHomeActivities(ArrayList())?.packageName
+
+    /**
+     * If the top activity should be exempt from desktop windowing and forced back to fullscreen.
+     * Currently includes all system ui, default home and transparent stack activities. However if
+     * the top activity is not being displayed, regardless of its configuration, we will not exempt
+     * it as to remain in the desktop windowing environment.
+     */
+    fun isTopActivityExemptFromDesktopWindowing(task: TaskInfo) =
+        isTopActivityExemptFromDesktopWindowing(task.baseActivity?.packageName,
+            task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent)
+
+    fun isTopActivityExemptFromDesktopWindowing(packageName: String?,
+        numActivities: Int, isTopActivityNoDisplay: Boolean, isActivityStackTransparent: Boolean) =
+        DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue
+                && ((isSystemUiTask(packageName)
+                || isPartOfDefaultHomePackage(packageName)
+                || isTransparentTask(isActivityStackTransparent, numActivities))
+                && !isTopActivityNoDisplay)
+
+    /**
+     * Returns true if all activities in a tasks stack are transparent. If there are no activities
+     * will return false.
+     */
+    fun isTransparentTask(task: TaskInfo): Boolean =
+        isTransparentTask(task.isActivityStackTransparent, task.numActivities)
+
+    private fun isTransparentTask(isActivityStackTransparent: Boolean, numActivities: Int) =
+        isActivityStackTransparent && numActivities > 0
+
+    private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage
+
+    /**
+     * Returns true if the tasks base activity is part of the default home package.
+     */
+    private fun isPartOfDefaultHomePackage(packageName: String?) =
+        packageName != null && packageName == defaultHomePackage
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index 1ee71ca..e196880 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -212,10 +212,18 @@
     }
 
     /**
+     * Return {@code true} if the current device supports the developer option for desktop mode.
+     */
+    private static boolean isDesktopModeDevOptionSupported(@NonNull Context context) {
+        return context.getResources().getBoolean(R.bool.config_isDesktopModeDevOptionSupported);
+    }
+
+    /**
      * Return {@code true} if desktop mode dev option should be shown on current device
      */
     public static boolean canShowDesktopModeDevOption(@NonNull Context context) {
-        return isDeviceEligibleForDesktopMode(context) && Flags.showDesktopWindowingDevOption();
+        return isDeviceEligibleForDesktopModeDevOption(context)
+                && Flags.showDesktopWindowingDevOption();
     }
 
     /**
@@ -226,17 +234,25 @@
     }
 
     /** Returns if desktop mode dev option should be enabled if there is no user override. */
-    public static boolean shouldDevOptionBeEnabledByDefault() {
-        return Flags.enableDesktopWindowingMode();
+    public static boolean shouldDevOptionBeEnabledByDefault(Context context) {
+        return isDeviceEligibleForDesktopMode(context) && Flags.enableDesktopWindowingMode();
     }
 
     /**
      * Return {@code true} if desktop mode is enabled and can be entered on the current device.
      */
     public static boolean canEnterDesktopMode(@NonNull Context context) {
-        if (!isDeviceEligibleForDesktopMode(context)) return false;
+        return (isDeviceEligibleForDesktopMode(context)
+                && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue())
+                || isDesktopModeEnabledByDevOption(context);
+    }
 
-        return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue();
+    /**
+     * Check if Desktop mode should be enabled because the dev option is shown and enabled.
+     */
+    private static boolean isDesktopModeEnabledByDevOption(@NonNull Context context) {
+        return DesktopModeFlags.isDesktopModeForcedEnabled()
+                && canShowDesktopModeDevOption(context);
     }
 
     /**
@@ -298,7 +314,21 @@
      * Return {@code true} if desktop mode is unrestricted and is supported in the device.
      */
     public static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
-        return !enforceDeviceRestrictions() || isDesktopModeSupported(context);
+        return !enforceDeviceRestrictions() || isDesktopModeSupported(context) || (
+                Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionSupported(
+                        context));
+    }
+
+    /**
+     * Return {@code true} if the developer option for desktop mode is unrestricted and is supported
+     * in the device.
+     *
+     * Note that, if {@link #isDeviceEligibleForDesktopMode(Context)} is true, then
+     * {@link #isDeviceEligibleForDesktopModeDevOption(Context)} is also true.
+     */
+    private static boolean isDeviceEligibleForDesktopModeDevOption(@NonNull Context context) {
+        return !enforceDeviceRestrictions() || isDesktopModeSupported(context)
+                || isDesktopModeDevOptionSupported(context);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index b4ef9f0f..55ed5fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -168,7 +168,8 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         mAnimationRunner.cancelAnimationFromMerge();
     }
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 8dabd54..d1c7f7d 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
@@ -1463,7 +1463,9 @@
 
         @Override
         public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-                @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+                @NonNull SurfaceControl.Transaction startT,
+                @NonNull SurfaceControl.Transaction finishT,
+                @NonNull IBinder mergeTarget,
                 @NonNull Transitions.TransitionFinishCallback finishCallback) {
             if (mClosePrepareTransition == transition) {
                 mClosePrepareTransition = null;
@@ -1476,7 +1478,7 @@
             if (info.getType() == TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION
                     && !mCloseTransitionRequested && info.getChanges().isEmpty() && mApps == null) {
                 finishCallback.onTransitionFinished(null);
-                t.apply();
+                startT.apply();
                 applyFinishOpenTransition();
                 return;
             }
@@ -1489,7 +1491,7 @@
             }
             // Handle the commit transition if this handler is running the open transition.
             finishCallback.onTransitionFinished(null);
-            t.apply();
+            startT.apply();
             if (mCloseTransitionRequested) {
                 if (mApps == null || mApps.length == 0) {
                     // animation was done
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index e8e25e20..c40a276 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -28,6 +28,7 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.Person;
+import android.app.TaskInfo;
 import android.content.Context;
 import android.content.Intent;
 import android.content.LocusId;
@@ -55,8 +56,10 @@
 import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.shared.bubbles.BubbleInfo;
 import com.android.wm.shell.shared.bubbles.ParcelableFlyoutMessage;
+import com.android.wm.shell.taskview.TaskView;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -204,6 +207,13 @@
     private Intent mAppIntent;
 
     /**
+     * Set while preparing a transition for animation. Several steps are needed before animation
+     * starts, so this is used to detect and route associated events to the coordinating transition.
+     */
+    @Nullable
+    private BubbleTransitions.BubbleTransition mPreparingTransition;
+
+    /**
      * Create a bubble with limited information based on given {@link ShortcutInfo}.
      * Note: Currently this is only being used when the bubble is persisted to disk.
      */
@@ -280,6 +290,30 @@
         mShortcutInfo = info;
     }
 
+    private Bubble(
+            TaskInfo task,
+            UserHandle user,
+            @Nullable Icon icon,
+            String key,
+            @ShellMainThread Executor mainExecutor,
+            @ShellBackgroundThread Executor bgExecutor) {
+        mGroupKey = null;
+        mLocusId = null;
+        mFlags = 0;
+        mUser = user;
+        mIcon = icon;
+        mIsAppBubble = true;
+        mKey = key;
+        mShowBubbleUpdateDot = false;
+        mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
+        mTaskId = task.taskId;
+        mAppIntent = null;
+        mDesiredHeight = Integer.MAX_VALUE;
+        mPackageName = task.baseActivity.getPackageName();
+    }
+
+
     /** Creates an app bubble. */
     public static Bubble createAppBubble(Intent intent, UserHandle user, @Nullable Icon icon,
             @ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
@@ -291,6 +325,16 @@
                 mainExecutor, bgExecutor);
     }
 
+    /** Creates a task bubble. */
+    public static Bubble createTaskBubble(TaskInfo info, UserHandle user, @Nullable Icon icon,
+            @ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
+        return new Bubble(info,
+                user,
+                icon,
+                getAppBubbleKeyForTask(info),
+                mainExecutor, bgExecutor);
+    }
+
     /** Creates a shortcut bubble. */
     public static Bubble createShortcutBubble(
             ShortcutInfo info,
@@ -316,6 +360,15 @@
         return info.getPackage() + ":" + info.getUserId() + ":" + info.getId();
     }
 
+    /**
+     * Returns the key for an app bubble from an app with package name, {@code packageName} on an
+     * Android user, {@code user}.
+     */
+    public static String getAppBubbleKeyForTask(TaskInfo taskInfo) {
+        Objects.requireNonNull(taskInfo);
+        return KEY_APP_BUBBLE + ":" + taskInfo.taskId;
+    }
+
     @VisibleForTesting(visibility = PRIVATE)
     public Bubble(@NonNull final BubbleEntry entry,
             final Bubbles.BubbleMetadataFlagListener listener,
@@ -469,6 +522,10 @@
         return mBubbleTaskView;
     }
 
+    public TaskView getTaskView() {
+        return mBubbleTaskView.getTaskView();
+    }
+
     /**
      * @return the ShortcutInfo id if it exists, or the metadata shortcut id otherwise.
      */
@@ -486,6 +543,10 @@
         return (mMetadataShortcutId != null && !mMetadataShortcutId.isEmpty());
     }
 
+    public BubbleTransitions.BubbleTransition getPreparingTransition() {
+        return mPreparingTransition;
+    }
+
     /**
      * Call this to clean up the task for the bubble. Ensure this is always called when done with
      * the bubble.
@@ -512,7 +573,8 @@
         mIntentActive = false;
     }
 
-    private void cleanupTaskView() {
+    /** Cleans-up the taskview associated with this bubble (possibly removing the task from wm) */
+    public void cleanupTaskView() {
         if (mBubbleTaskView != null) {
             mBubbleTaskView.cleanup();
             mBubbleTaskView = null;
@@ -533,7 +595,7 @@
      * <p>If we're switching between bar and floating modes, pass {@code false} on
      * {@code cleanupTaskView} to avoid recreating it in the new mode.
      */
-    void cleanupViews(boolean cleanupTaskView) {
+    public void cleanupViews(boolean cleanupTaskView) {
         cleanupExpandedView(cleanupTaskView);
         mIconView = null;
     }
@@ -556,6 +618,13 @@
     }
 
     /**
+     * Sets the current bubble-transition that is coordinating a change in this bubble.
+     */
+    void setPreparingTransition(BubbleTransitions.BubbleTransition transit) {
+        mPreparingTransition = transit;
+    }
+
+    /**
      * Sets whether this bubble is considered text changed. This method is purely for
      * testing.
      */
@@ -1025,7 +1094,7 @@
      * intent for an app. In this case we don't show a badge on the icon.
      */
     public boolean isAppLaunchIntent() {
-        if (Flags.enableBubbleAnything() && mAppIntent != null) {
+        if (BubbleAnythingFlagHelper.enableCreateAnyBubble() && mAppIntent != null) {
             return mAppIntent.hasCategory("android.intent.category.LAUNCHER");
         }
         return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 4f9028e..e3f8e0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -40,9 +40,11 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.PendingIntent;
+import android.app.TaskInfo;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -78,6 +80,8 @@
 import android.view.WindowManager;
 import android.window.ScreenCapture;
 import android.window.ScreenCapture.SynchronousScreenCaptureListener;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
 
 import androidx.annotation.MainThread;
 import androidx.annotation.Nullable;
@@ -110,6 +114,7 @@
 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
 import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
 import com.android.wm.shell.sysui.ConfigurationChangeListener;
@@ -287,6 +292,8 @@
     /** Used to send updates to the views from {@link #mBubbleDataListener}. */
     private BubbleViewCallback mBubbleViewCallback;
 
+    private final BubbleTransitions mBubbleTransitions;
+
     public BubbleController(Context context,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
@@ -350,12 +357,16 @@
                 context.getResources().getDimensionPixelSize(
                         com.android.internal.R.dimen.importance_ring_stroke_width));
         mDisplayController = displayController;
+        final TaskViewTransitions tvTransitions;
         if (TaskViewTransitions.useRepo()) {
-            mTaskViewController = new TaskViewTransitions(transitions, taskViewRepository,
-                    organizer, syncQueue);
+            tvTransitions = new TaskViewTransitions(transitions, taskViewRepository, organizer,
+                    syncQueue);
         } else {
-            mTaskViewController = taskViewTransitions;
+            tvTransitions = taskViewTransitions;
         }
+        mTaskViewController = new BubbleTaskViewController(tvTransitions);
+        mBubbleTransitions = new BubbleTransitions(transitions, organizer, taskViewRepository, data,
+                tvTransitions, context);
         mTransitions = transitions;
         mOneHandedOptional = oneHandedOptional;
         mDragAndDropController = dragAndDropController;
@@ -629,6 +640,14 @@
         mOnImeHidden = onImeHidden;
         mBubblePositioner.setImeVisible(false /* visible */, 0 /* height */);
         int displayId = mWindowManager.getDefaultDisplay().getDisplayId();
+        // if the device is locked we can't use the status bar service to hide the IME because
+        // the IME state is frozen and it will lead to internal IME state going out of sync. This
+        // will make the IME visible when the device is unlocked. Instead we use
+        // DisplayImeController directly to make sure the state is correct when the device unlocks.
+        if (isDeviceLocked()) {
+            mDisplayImeController.hideImeForBubblesWhenLocked(displayId);
+            return;
+        }
         try {
             mBarService.hideCurrentInputMethodForBubbles(displayId);
         } catch (RemoteException e) {
@@ -668,8 +687,20 @@
                         ? mNotifEntryToExpandOnShadeUnlock.getKey() : "null"));
         mIsStatusBarShade = isShade;
         if (!mIsStatusBarShade && didChange) {
-            // Only collapse stack on change
-            collapseStack();
+            if (mBubbleData.isExpanded()) {
+                // If the IME is visible, hide it first and then collapse.
+                if (mBubblePositioner.isImeVisible()) {
+                    hideCurrentInputMethod(this::collapseStack);
+                } else {
+                    collapseStack();
+                }
+            } else if (mOnImeHidden != null) {
+                // a request to collapse started before we're notified that the device is locking.
+                // we're currently waiting for the IME to collapse, before mOnImeHidden can be
+                // executed, which may not happen since the screen may already be off. hide the IME
+                // immediately now that we're locked and pass the same runnable so it can complete.
+                hideCurrentInputMethod(mOnImeHidden);
+            }
         }
 
         if (mNotifEntryToExpandOnShadeUnlock != null) {
@@ -1422,7 +1453,7 @@
      * @param info the shortcut info for the bubble.
      */
     public void expandStackAndSelectBubble(ShortcutInfo info) {
-        if (!Flags.enableBubbleAnything()) return;
+        if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
         Bubble b = mBubbleData.getOrCreateBubble(info); // Removes from overflow
         ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - shortcut=%s", info);
         if (b.isInflated()) {
@@ -1439,7 +1470,7 @@
      * @param intent the intent for the bubble.
      */
     public void expandStackAndSelectBubble(Intent intent, UserHandle user) {
-        if (!Flags.enableBubbleAnything()) return;
+        if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
         Bubble b = mBubbleData.getOrCreateBubble(intent, user); // Removes from overflow
         ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", intent);
         if (b.isInflated()) {
@@ -1456,7 +1487,19 @@
      * @param taskInfo the task.
      */
     public void expandStackAndSelectBubble(ActivityManager.RunningTaskInfo taskInfo) {
-        // TODO(384976265): Not implemented yet
+        if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) return;
+        Bubble b = mBubbleData.getOrCreateBubble(taskInfo); // Removes from overflow
+        ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", taskInfo.taskId);
+        if (b.isInflated()) {
+            mBubbleData.setSelectedBubbleAndExpandStack(b);
+        } else {
+            b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+            // Lazy init stack view when a bubble is created
+            ensureBubbleViewsAndWindowCreated();
+            mBubbleTransitions.startConvertToBubble(b, taskInfo, mExpandedViewManager,
+                    mBubbleTaskViewFactory, mBubblePositioner, mLogger, mStackView, mLayerView,
+                    mBubbleIconFactory, mInflateSynchronously);
+        }
     }
 
     /**
@@ -2057,7 +2100,12 @@
         @Override
         public void removeBubble(Bubble removedBubble) {
             if (mLayerView != null) {
+                final BubbleTransitions.BubbleTransition bubbleTransit =
+                        removedBubble.getPreparingTransition();
                 mLayerView.removeBubble(removedBubble, () -> {
+                    if (bubbleTransit != null) {
+                        bubbleTransit.continueCollapse();
+                    }
                     if (!mBubbleData.hasBubbles() && !isStackExpanded()) {
                         mLayerView.setVisibility(INVISIBLE);
                         removeFromWindowManagerMaybe();
@@ -2261,9 +2309,16 @@
 
     private void showExpandedViewForBubbleBar() {
         BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
-        if (selectedBubble != null && mLayerView != null) {
-            mLayerView.showExpandedView(selectedBubble);
+        if (selectedBubble == null) return;
+        if (selectedBubble instanceof Bubble) {
+            final Bubble bubble = (Bubble) selectedBubble;
+            if (bubble.getPreparingTransition() != null) {
+                bubble.getPreparingTransition().continueExpand();
+                return;
+            }
         }
+        if (mLayerView == null) return;
+        mLayerView.showExpandedView(selectedBubble);
     }
 
     private void collapseExpandedViewForBubbleBar() {
@@ -2448,6 +2503,10 @@
         mBubbleData.setSelectedBubbleAndExpandStack(bubbleToSelect);
     }
 
+    private boolean isDeviceLocked() {
+        return !mIsStatusBarShade;
+    }
+
     /**
      * Description of current bubble state.
      */
@@ -2481,7 +2540,7 @@
      * @param entry   the entry to bubble.
      */
     static boolean canLaunchInTaskView(Context context, BubbleEntry entry) {
-        if (Flags.enableBubbleAnything()) return true;
+        if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) return true;
         PendingIntent intent = entry.getBubbleMetadata() != null
                 ? entry.getBubbleMetadata().getIntent()
                 : null;
@@ -2613,6 +2672,17 @@
                     public void animateBubbleBarLocation(BubbleBarLocation location) {
                         mListener.call(l -> l.animateBubbleBarLocation(location));
                     }
+
+                    @Override
+                    public void onDragItemOverBubbleBarDragZone(
+                            @NonNull BubbleBarLocation location) {
+                        mListener.call(l -> l.onDragItemOverBubbleBarDragZone(location));
+                    }
+
+                    @Override
+                    public void onItemDraggedOutsideBubbleBarDropZone() {
+                        mListener.call(IBubblesListener::onItemDraggedOutsideBubbleBarDropZone);
+                    }
                 };
 
         IBubblesImpl(BubbleController controller) {
@@ -2665,7 +2735,18 @@
 
         @Override
         public void collapseBubbles() {
-            mMainExecutor.execute(() -> mController.collapseStack());
+            mMainExecutor.execute(() -> {
+                if (mBubbleData.getSelectedBubble() instanceof Bubble) {
+                    if (((Bubble) mBubbleData.getSelectedBubble()).getPreparingTransition()
+                            != null) {
+                        // Currently preparing a transition which will, itself, collapse the bubble.
+                        // For transition preparation, the timing of bubble-collapse must be in
+                        // sync with the rest of the set-up.
+                        return;
+                    }
+                }
+                mController.collapseStack();
+            });
         }
 
         @Override
@@ -3057,4 +3138,84 @@
             return mKeyToShownInShadeMap.get(key);
         }
     }
+
+    private class BubbleTaskViewController implements TaskViewController {
+        private final TaskViewTransitions mBaseTransitions;
+
+        BubbleTaskViewController(TaskViewTransitions baseTransitions) {
+            mBaseTransitions = baseTransitions;
+        }
+
+        @Override
+        public void registerTaskView(TaskViewTaskController tv) {
+            mBaseTransitions.registerTaskView(tv);
+        }
+
+        @Override
+        public void unregisterTaskView(TaskViewTaskController tv) {
+            mBaseTransitions.unregisterTaskView(tv);
+        }
+
+        @Override
+        public void startShortcutActivity(@NonNull TaskViewTaskController destination,
+                @NonNull ShortcutInfo shortcut, @NonNull ActivityOptions options,
+                @Nullable Rect launchBounds) {
+            mBaseTransitions.startShortcutActivity(destination, shortcut, options, launchBounds);
+        }
+
+        @Override
+        public void startActivity(@NonNull TaskViewTaskController destination,
+                @NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+                @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
+            mBaseTransitions.startActivity(destination, pendingIntent, fillInIntent,
+                    options, launchBounds);
+        }
+
+        @Override
+        public void startRootTask(@NonNull TaskViewTaskController destination,
+                ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+                @Nullable WindowContainerTransaction wct) {
+            mBaseTransitions.startRootTask(destination, taskInfo, leash, wct);
+        }
+
+        @Override
+        public void removeTaskView(@NonNull TaskViewTaskController taskView,
+                @Nullable WindowContainerToken taskToken) {
+            mBaseTransitions.removeTaskView(taskView, taskToken);
+        }
+
+        @Override
+        public void moveTaskViewToFullscreen(@NonNull TaskViewTaskController taskView) {
+            final TaskInfo tinfo = taskView.getTaskInfo();
+            if (tinfo == null) {
+                return;
+            }
+            Bubble bub = null;
+            for (Bubble b : mBubbleData.getBubbles()) {
+                if (b.getTaskId() == tinfo.taskId) {
+                    bub = b;
+                    break;
+                }
+            }
+            if (bub == null) {
+                return;
+            }
+            mBubbleTransitions.startConvertFromBubble(bub, tinfo);
+        }
+
+        @Override
+        public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
+            mBaseTransitions.setTaskViewVisible(taskView, visible);
+        }
+
+        @Override
+        public void setTaskBounds(TaskViewTaskController taskView, Rect boundsOnScreen) {
+            mBaseTransitions.setTaskBounds(taskView, boundsOnScreen);
+        }
+
+        @Override
+        public boolean isUsingShellTransitions() {
+            return mBaseTransitions.isUsingShellTransitions();
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 7430209..96d0f6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -22,6 +22,7 @@
 
 import android.annotation.NonNull;
 import android.app.PendingIntent;
+import android.app.TaskInfo;
 import android.content.Context;
 import android.content.Intent;
 import android.content.LocusId;
@@ -470,6 +471,17 @@
         return bubbleToReturn;
     }
 
+    Bubble getOrCreateBubble(TaskInfo taskInfo) {
+        UserHandle user = UserHandle.of(mCurrentUserId);
+        String bubbleKey = Bubble.getAppBubbleKeyForTask(taskInfo);
+        Bubble bubbleToReturn = findAndRemoveBubbleFromOverflow(bubbleKey);
+        if (bubbleToReturn == null) {
+            bubbleToReturn = Bubble.createTaskBubble(taskInfo, user, null, mMainExecutor,
+                    mBgExecutor);
+        }
+        return bubbleToReturn;
+    }
+
     @Nullable
     private Bubble findAndRemoveBubbleFromOverflow(String key) {
         Bubble bubbleToReturn = getBubbleInStackWithKey(key);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 13f8e9e..97b03a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -71,6 +71,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.AlphaOptimizedButton;
 import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.taskview.TaskView;
 
 import java.io.PrintWriter;
@@ -226,7 +227,8 @@
                             MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
 
                     final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
-                            || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
+                            || (mBubble.getShortcutInfo() != null
+                            && BubbleAnythingFlagHelper.enableCreateAnyBubble()));
 
                     if (mBubble.isAppBubble()) {
                         Context context =
@@ -687,11 +689,6 @@
         }
     }
 
-    /** Sets the alpha for the pointer. */
-    public void setPointerAlpha(float alpha) {
-        mPointerView.setAlpha(alpha);
-    }
-
     /**
      * Get alpha from underlying {@code TaskView} if this view is for a bubble.
      * Or get alpha for the overflow view if this view is for overflow.
@@ -794,24 +791,6 @@
         onContainerClipUpdate();
     }
 
-    /**
-     * Sets the clipping for the view.
-     */
-    public void setTaskViewClip(Rect rect) {
-        mLeftClip = rect.left;
-        mTopClip = rect.top;
-        mRightClip = rect.right;
-        mBottomClip = rect.bottom;
-        onContainerClipUpdate();
-    }
-
-    /**
-     * Returns a rect representing the clipping for the view.
-     */
-    public Rect getTaskViewClip() {
-        return new Rect(mLeftClip, mTopClip, mRightClip, mBottom);
-    }
-
     private void onContainerClipUpdate() {
         if (mTopClip == 0 && mBottomClip == 0 && mRightClip == 0 && mLeftClip == 0) {
             if (mIsClipping) {
@@ -1122,13 +1101,6 @@
     }
 
     /**
-     * Return width of the current pointer
-     */
-    public int getPointerWidth() {
-        return mPointerWidth;
-    }
-
-    /**
      * Position of the manage button displayed in the expanded view. Used for placing user
      * education about the manage button.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 6299531..086c919 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -137,14 +137,15 @@
         // Update bitmap
         val fg = InsetDrawable(overflowBtn?.iconDrawable, overflowIconInset)
         val drawable = AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)
-        bitmap = iconFactory.createBadgedIconBitmap(drawable).icon
+        val bubbleBitmapScale = FloatArray(1)
+        bitmap = iconFactory.getBubbleBitmap(drawable, bubbleBitmapScale)
 
         // Update dot path
         dotPath =
             PathParser.createPathFromPathData(
                 res.getString(com.android.internal.R.string.config_icon_mask)
             )
-        val scale = iconFactory.normalizer.getScale(iconView!!.iconDrawable)
+        val scale = bubbleBitmapScale[0]
         val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
         val matrix = Matrix()
         matrix.setScale(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 1a61793..a725e04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -87,6 +87,7 @@
     private int mExpandedViewLargeScreenWidth;
     private int mExpandedViewLargeScreenInsetClosestEdge;
     private int mExpandedViewLargeScreenInsetFurthestEdge;
+    private int mExpandedViewBubbleBarWidth;
 
     private int mOverflowWidth;
     private int mExpandedViewPadding;
@@ -158,12 +159,13 @@
         mBubbleOffscreenAmount = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
         mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
         mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
+        mExpandedViewBubbleBarWidth = Math.min(
+                res.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width),
+                mPositionRect.width() - 2 * mExpandedViewPadding
+        );
 
         if (mShowingInBubbleBar) {
-            mExpandedViewLargeScreenWidth = Math.min(
-                    res.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width),
-                    mPositionRect.width() - 2 * mExpandedViewPadding
-            );
+            mExpandedViewLargeScreenWidth = mExpandedViewBubbleBarWidth;
         } else if (mDeviceConfig.isSmallTablet()) {
             mExpandedViewLargeScreenWidth = (int) (bounds.width()
                     * EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT);
@@ -888,7 +890,7 @@
      * How wide the expanded view should be when showing from the bubble bar.
      */
     public int getExpandedViewWidthForBubbleBar(boolean isOverflow) {
-        return isOverflow ? mOverflowWidth : mExpandedViewLargeScreenWidth;
+        return isOverflow ? mOverflowWidth : mExpandedViewBubbleBarWidth;
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 89c038b..a6b8585 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -36,7 +36,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.protolog.ProtoLog;
-import com.android.wm.shell.Flags;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.taskview.TaskView;
 
 /**
@@ -108,8 +108,11 @@
                     options.setPendingIntentBackgroundActivityStartMode(
                             MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
                     final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
-                            || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
-                    if (mBubble.isAppBubble()) {
+                            || (mBubble.getShortcutInfo() != null
+                            && BubbleAnythingFlagHelper.enableCreateAnyBubble()));
+                    if (mBubble.getPreparingTransition() != null) {
+                        mBubble.getPreparingTransition().surfaceCreated();
+                    } else if (mBubble.isAppBubble()) {
                         Context context =
                                 mContext.createContextAsUser(
                                         mBubble.getUser(), Context.CONTEXT_RESTRICTED);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
new file mode 100644
index 0000000..48b83ce
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2025 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.bubbles;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.View.INVISIBLE;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.SurfaceView;
+import android.view.View;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
+import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.taskview.TaskView;
+import com.android.wm.shell.taskview.TaskViewRepository;
+import com.android.wm.shell.taskview.TaskViewTaskController;
+import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Implements transition coordination for bubble operations.
+ */
+public class BubbleTransitions {
+    private static final String TAG = "BubbleTransitions";
+
+    /**
+     * Multiplier used to convert a view elevation to an "equivalent" shadow-radius. This is the
+     * same multiple used by skia and surface-outsets in WMS.
+     */
+    private static final float ELEVATION_TO_RADIUS = 2;
+
+    @NonNull final Transitions mTransitions;
+    @NonNull final ShellTaskOrganizer mTaskOrganizer;
+    @NonNull final TaskViewRepository mRepository;
+    @NonNull final Executor mMainExecutor;
+    @NonNull final BubbleData mBubbleData;
+    @NonNull final TaskViewTransitions mTaskViewTransitions;
+    @NonNull final Context mContext;
+
+    BubbleTransitions(@NonNull Transitions transitions, @NonNull ShellTaskOrganizer organizer,
+            @NonNull TaskViewRepository repository, @NonNull BubbleData bubbleData,
+            @NonNull TaskViewTransitions taskViewTransitions, Context context) {
+        mTransitions = transitions;
+        mTaskOrganizer = organizer;
+        mRepository = repository;
+        mMainExecutor = transitions.getMainExecutor();
+        mBubbleData = bubbleData;
+        mTaskViewTransitions = taskViewTransitions;
+        mContext = context;
+    }
+
+    /**
+     * Starts a convert-to-bubble transition.
+     *
+     * @see ConvertToBubble
+     */
+    public BubbleTransition startConvertToBubble(Bubble bubble, TaskInfo taskInfo,
+            BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory,
+            BubblePositioner positioner, BubbleLogger logger, BubbleStackView stackView,
+            BubbleBarLayerView layerView, BubbleIconFactory iconFactory,
+            boolean inflateSync) {
+        ConvertToBubble convert = new ConvertToBubble(bubble, taskInfo, mContext,
+                expandedViewManager, factory, positioner, logger, stackView, layerView, iconFactory,
+                inflateSync);
+        return convert;
+    }
+
+    /**
+     * Starts a convert-from-bubble transition.
+     *
+     * @see ConvertFromBubble
+     */
+    public BubbleTransition startConvertFromBubble(Bubble bubble,
+            TaskInfo taskInfo) {
+        ConvertFromBubble convert = new ConvertFromBubble(bubble, taskInfo);
+        return convert;
+    }
+
+    /**
+     * Plucks the task-surface out of an ancestor view while making the view invisible. This helper
+     * attempts to do this seamlessly (ie. view becomes invisible in sync with task reparent).
+     */
+    private void pluck(SurfaceControl taskLeash, View fromView, SurfaceControl dest,
+            float destX, float destY, float cornerRadius, SurfaceControl.Transaction t,
+            Runnable onPlucked) {
+        SurfaceControl.Transaction pluckT = new SurfaceControl.Transaction();
+        pluckT.reparent(taskLeash, dest);
+        t.reparent(taskLeash, dest);
+        pluckT.setPosition(taskLeash, destX, destY);
+        t.setPosition(taskLeash, destX, destY);
+        pluckT.show(taskLeash);
+        pluckT.setAlpha(taskLeash, 1.f);
+        float shadowRadius = fromView.getElevation() * ELEVATION_TO_RADIUS;
+        pluckT.setShadowRadius(taskLeash, shadowRadius);
+        pluckT.setCornerRadius(taskLeash, cornerRadius);
+        t.setShadowRadius(taskLeash, shadowRadius);
+        t.setCornerRadius(taskLeash, cornerRadius);
+
+        // Need to remove the taskview AFTER applying the startTransaction because it isn't
+        // synchronized.
+        pluckT.addTransactionCommittedListener(mMainExecutor, onPlucked::run);
+        fromView.getViewRootImpl().applyTransactionOnDraw(pluckT);
+        fromView.setVisibility(INVISIBLE);
+    }
+
+    /**
+     * Interface to a bubble-specific transition. Bubble transitions have a multi-step lifecycle
+     * in order to coordinate with the bubble view logic. These steps are communicated on this
+     * interface.
+     */
+    interface BubbleTransition {
+        default void surfaceCreated() {}
+        default void continueExpand() {}
+        void skip();
+        default void continueCollapse() {}
+    }
+
+    /**
+     * BubbleTransition that coordinates the process of a non-bubble task becoming a bubble. The
+     * steps are as follows:
+     *
+     * 1. Start inflating the bubble view
+     * 2. Once inflated (but not-yet visible), tell WM to do the shell-transition.
+     * 3. Transition becomes ready, so notify Launcher
+     * 4. Launcher responds with showExpandedView which calls continueExpand() to make view visible
+     * 5. Surface is created which kicks off actual animation
+     *
+     * So, constructor -> onInflated -> startAnimation -> continueExpand -> surfaceCreated.
+     *
+     * continueExpand and surfaceCreated are set-up to happen in either order, though, to support
+     * UX/timing adjustments.
+     */
+    @VisibleForTesting
+    class ConvertToBubble implements Transitions.TransitionHandler, BubbleTransition {
+        final BubbleBarLayerView mLayerView;
+        Bubble mBubble;
+        IBinder mTransition;
+        Transitions.TransitionFinishCallback mFinishCb;
+        WindowContainerTransaction mFinishWct = null;
+        final Rect mStartBounds = new Rect();
+        SurfaceControl mSnapshot = null;
+        TaskInfo mTaskInfo;
+        boolean mFinishedExpand = false;
+        BubbleViewProvider mPriorBubble = null;
+
+        private SurfaceControl.Transaction mFinishT;
+        private SurfaceControl mTaskLeash;
+
+        ConvertToBubble(Bubble bubble, TaskInfo taskInfo, Context context,
+                BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory,
+                BubblePositioner positioner, BubbleLogger logger, BubbleStackView stackView,
+                BubbleBarLayerView layerView, BubbleIconFactory iconFactory, boolean inflateSync) {
+            mBubble = bubble;
+            mTaskInfo = taskInfo;
+            mLayerView = layerView;
+            mBubble.setInflateSynchronously(inflateSync);
+            mBubble.setPreparingTransition(this);
+            mBubble.inflate(
+                    this::onInflated,
+                    context,
+                    expandedViewManager,
+                    factory,
+                    positioner,
+                    logger,
+                    stackView,
+                    layerView,
+                    iconFactory,
+                    false /* skipInflation */);
+        }
+
+        @VisibleForTesting
+        void onInflated(Bubble b) {
+            if (b != mBubble) {
+                throw new IllegalArgumentException("inflate callback doesn't match bubble");
+            }
+            final Rect launchBounds = new Rect();
+            mLayerView.getExpandedViewRestBounds(launchBounds);
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+                if (mTaskInfo.getParentTaskId() != INVALID_TASK_ID) {
+                    wct.reparent(mTaskInfo.token, null, true);
+                }
+            }
+
+            wct.setAlwaysOnTop(mTaskInfo.token, true);
+            wct.setWindowingMode(mTaskInfo.token, WINDOWING_MODE_MULTI_WINDOW);
+            wct.setBounds(mTaskInfo.token, launchBounds);
+
+            final TaskView tv = b.getTaskView();
+            tv.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT);
+            final TaskViewRepository.TaskViewState state = mRepository.byTaskView(
+                    tv.getController());
+            if (state != null) {
+                state.mVisible = true;
+            }
+            mTaskViewTransitions.enqueueExternal(tv.getController(), () -> {
+                mTransition = mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
+                return mTransition;
+            });
+        }
+
+        @Override
+        public void skip() {
+            mBubble.setPreparingTransition(null);
+            mFinishCb.onTransitionFinished(mFinishWct);
+            mFinishCb = null;
+        }
+
+        @Override
+        public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+                @Nullable TransitionRequestInfo request) {
+            return null;
+        }
+
+        @Override
+        public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startT,
+                @NonNull SurfaceControl.Transaction finishT,
+                @NonNull IBinder mergeTarget,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        }
+
+        @Override
+        public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+                @NonNull SurfaceControl.Transaction finishTransaction) {
+            if (!aborted) return;
+            mTransition = null;
+            mTaskViewTransitions.onExternalDone(transition);
+        }
+
+        @Override
+        public boolean startAnimation(@NonNull IBinder transition,
+                @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            if (mTransition != transition) return false;
+            boolean found = false;
+            for (int i = 0; i < info.getChanges().size(); ++i) {
+                final TransitionInfo.Change chg = info.getChanges().get(i);
+                if (chg.getTaskInfo() == null) continue;
+                if (chg.getMode() != TRANSIT_CHANGE) continue;
+                if (!mTaskInfo.token.equals(chg.getTaskInfo().token)) continue;
+                mStartBounds.set(chg.getStartAbsBounds());
+                // Converting a task into taskview, so treat as "new"
+                mFinishWct = new WindowContainerTransaction();
+                mTaskInfo = chg.getTaskInfo();
+                mFinishT = finishTransaction;
+                mTaskLeash = chg.getLeash();
+                found = true;
+                mSnapshot = chg.getSnapshot();
+                break;
+            }
+            if (!found) {
+                Slog.w(TAG, "Expected a TaskView conversion in this transition but didn't get "
+                        + "one, cleaning up the task view");
+                mBubble.getTaskView().getController().setTaskNotFound();
+                mTaskViewTransitions.onExternalDone(transition);
+                return false;
+            }
+            mFinishCb = finishCallback;
+
+            // Now update state (and talk to launcher) in parallel with snapshot stuff
+            mBubbleData.notificationEntryUpdated(mBubble, /* suppressFlyout= */ true,
+                    /* showInShade= */ false);
+
+            startTransaction.show(mSnapshot);
+            // Move snapshot to root so that it remains visible while task is moved to taskview
+            startTransaction.reparent(mSnapshot, info.getRoot(0).getLeash());
+            startTransaction.setPosition(mSnapshot,
+                    mStartBounds.left - info.getRoot(0).getOffset().x,
+                    mStartBounds.top - info.getRoot(0).getOffset().y);
+            startTransaction.setLayer(mSnapshot, Integer.MAX_VALUE);
+            startTransaction.apply();
+
+            mTaskViewTransitions.onExternalDone(transition);
+            return true;
+        }
+
+        @Override
+        public void continueExpand() {
+            mFinishedExpand = true;
+            final boolean animate = mLayerView.canExpandView(mBubble);
+            if (animate) {
+                mPriorBubble = mLayerView.prepareConvertedView(mBubble);
+            }
+            if (mPriorBubble != null) {
+                // TODO: an animation. For now though, just remove it.
+                final BubbleBarExpandedView priorView = mPriorBubble.getBubbleBarExpandedView();
+                mLayerView.removeView(priorView);
+                mPriorBubble = null;
+            }
+            if (!animate || mBubble.getTaskView().getSurfaceControl() != null) {
+                playAnimation(animate);
+            }
+        }
+
+        @Override
+        public void surfaceCreated() {
+            mMainExecutor.execute(() -> {
+                final TaskViewTaskController tvc = mBubble.getTaskView().getController();
+                final TaskViewRepository.TaskViewState state = mRepository.byTaskView(tvc);
+                if (state == null) return;
+                state.mVisible = true;
+                if (mFinishedExpand) {
+                    playAnimation(true /* animate */);
+                }
+            });
+        }
+
+        private void playAnimation(boolean animate) {
+            final TaskViewTaskController tv = mBubble.getTaskView().getController();
+            final SurfaceControl.Transaction startT = new SurfaceControl.Transaction();
+            mTaskViewTransitions.prepareOpenAnimation(tv, true /* new */, startT, mFinishT,
+                    (ActivityManager.RunningTaskInfo) mTaskInfo, mTaskLeash, mFinishWct);
+
+            if (mFinishWct.isEmpty()) {
+                mFinishWct = null;
+            }
+
+            // Preparation is complete.
+            mBubble.setPreparingTransition(null);
+
+            if (animate) {
+                mLayerView.animateConvert(startT, mStartBounds, mSnapshot, mTaskLeash, () -> {
+                    mFinishCb.onTransitionFinished(mFinishWct);
+                    mFinishCb = null;
+                });
+            } else {
+                startT.apply();
+                mFinishCb.onTransitionFinished(mFinishWct);
+                mFinishCb = null;
+            }
+        }
+    }
+
+    /**
+     * BubbleTransition that coordinates the setup for moving a task out of a bubble. The actual
+     * animation is owned by the "receiver" of the task; however, because Bubbles uses TaskView,
+     * we need to do some extra coordination work to get the task surface out of the view
+     * "seamlessly".
+     *
+     * The process here looks like:
+     * 1. Send transition to WM for leaving bubbles mode
+     * 2. in startAnimation, set-up a "pluck" operation to pull the task surface out of taskview
+     * 3. Once "plucked", remove the view (calls continueCollapse when surfaces can be cleaned-up)
+     * 4. Then re-dispatch the transition animation so that the "receiver" can animate it.
+     *
+     * So, constructor -> startAnimation -> continueCollapse -> re-dispatch.
+     */
+    @VisibleForTesting
+    class ConvertFromBubble implements Transitions.TransitionHandler, BubbleTransition {
+        @NonNull final Bubble mBubble;
+        IBinder mTransition;
+        TaskInfo mTaskInfo;
+        SurfaceControl mTaskLeash;
+        SurfaceControl mRootLeash;
+
+        ConvertFromBubble(@NonNull Bubble bubble, TaskInfo taskInfo) {
+            mBubble = bubble;
+            mTaskInfo = taskInfo;
+
+            mBubble.setPreparingTransition(this);
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            WindowContainerToken token = mTaskInfo.getToken();
+            wct.setWindowingMode(token, WINDOWING_MODE_UNDEFINED);
+            wct.setAlwaysOnTop(token, false);
+            mTaskOrganizer.setInterceptBackPressedOnTaskRoot(token, false);
+            mTaskViewTransitions.enqueueExternal(
+                    mBubble.getTaskView().getController(),
+                    () -> {
+                        mTransition = mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
+                        return mTransition;
+                    });
+        }
+
+        @Override
+        public void skip() {
+            mBubble.setPreparingTransition(null);
+            final TaskViewTaskController tv =
+                    mBubble.getTaskView().getController();
+            tv.notifyTaskRemovalStarted(tv.getTaskInfo());
+            mTaskLeash = null;
+        }
+
+        @Override
+        public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+                @android.annotation.Nullable TransitionRequestInfo request) {
+            return null;
+        }
+
+        @Override
+        public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startT,
+                @NonNull SurfaceControl.Transaction finishT,
+                @NonNull IBinder mergeTarget,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        }
+
+        @Override
+        public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+                @NonNull SurfaceControl.Transaction finishTransaction) {
+            if (!aborted) return;
+            mTransition = null;
+            skip();
+            mTaskViewTransitions.onExternalDone(transition);
+        }
+
+        @Override
+        public boolean startAnimation(@NonNull IBinder transition,
+                @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            if (mTransition != transition) return false;
+
+            final TaskViewTaskController tv =
+                    mBubble.getTaskView().getController();
+            if (tv == null) {
+                mTaskViewTransitions.onExternalDone(transition);
+                return false;
+            }
+
+            TransitionInfo.Change taskChg = null;
+
+            boolean found = false;
+            for (int i = 0; i < info.getChanges().size(); ++i) {
+                final TransitionInfo.Change chg = info.getChanges().get(i);
+                if (chg.getTaskInfo() == null) continue;
+                if (chg.getMode() != TRANSIT_CHANGE) continue;
+                if (!mTaskInfo.token.equals(chg.getTaskInfo().token)) continue;
+                found = true;
+                mRepository.remove(tv);
+                taskChg = chg;
+                break;
+            }
+
+            if (!found) {
+                Slog.w(TAG, "Expected a TaskView conversion in this transition but didn't get "
+                        + "one, cleaning up the task view");
+                tv.setTaskNotFound();
+                skip();
+                mTaskViewTransitions.onExternalDone(transition);
+                return false;
+            }
+
+            mTaskLeash = taskChg.getLeash();
+            mRootLeash = info.getRoot(0).getLeash();
+
+            SurfaceControl dest =
+                    mBubble.getBubbleBarExpandedView().getViewRootImpl().getSurfaceControl();
+            final Runnable onPlucked = () -> {
+                // Need to remove the taskview AFTER applying the startTransaction because
+                // it isn't synchronized.
+                tv.notifyTaskRemovalStarted(tv.getTaskInfo());
+                // Unset after removeView so it can be used to pick a different animation.
+                mBubble.setPreparingTransition(null);
+                mBubbleData.setExpanded(false /* expanded */);
+            };
+            if (dest != null) {
+                pluck(mTaskLeash, mBubble.getBubbleBarExpandedView(), dest,
+                        taskChg.getStartAbsBounds().left - info.getRoot(0).getOffset().x,
+                        taskChg.getStartAbsBounds().top - info.getRoot(0).getOffset().y,
+                        mBubble.getBubbleBarExpandedView().getCornerRadius(), startTransaction,
+                        onPlucked);
+                mBubble.getBubbleBarExpandedView().post(() -> mTransitions.dispatchTransition(
+                        mTransition, info, startTransaction, finishTransaction, finishCallback,
+                        null));
+            } else {
+                onPlucked.run();
+                mTransitions.dispatchTransition(mTransition, info, startTransaction,
+                        finishTransaction, finishCallback, null);
+            }
+
+            mTaskViewTransitions.onExternalDone(transition);
+            return true;
+        }
+
+        @Override
+        public void continueCollapse() {
+            mBubble.cleanupTaskView();
+            if (mTaskLeash == null) return;
+            SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            t.reparent(mTaskLeash, mRootLeash);
+            t.apply();
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 62895fe..4297fac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -36,6 +36,7 @@
 import android.window.ScreenCapture.SynchronousScreenCaptureListener;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.wm.shell.shared.annotations.ExternalThread;
@@ -330,6 +331,18 @@
          * Does not result in a state change.
          */
         void animateBubbleBarLocation(BubbleBarLocation location);
+
+        /**
+         * Called when an application icon is being dragged over the Bubble Bar drop zone.
+         * The location of the Bubble Bar is provided as an argument.
+         */
+        void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation location);
+
+        /**
+         * Called when an application icon is being dragged outside the Bubble Bar drop zone.
+         * Always called after {@link #onDragItemOverBubbleBarDragZone(BubbleBarLocation)}
+         */
+        void onItemDraggedOutsideBubbleBarDropZone();
     }
 
     /** Listener to find out about stack expansion / collapse events. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
index eb907db..9fc769f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
@@ -33,4 +33,16 @@
      * Does not result in a state change.
      */
     void animateBubbleBarLocation(in BubbleBarLocation location);
+
+    /**
+     * Called when an application icon is being dragged over the Bubble Bar drop zone.
+     * The location of the Bubble Bar is provided as an argument.
+     */
+    void onDragItemOverBubbleBarDragZone(in BubbleBarLocation location);
+
+    /**
+     * Called when an application icon is being dragged outside the Bubble Bar drop zone.
+     * Always called after {@link #onDragItemOverBubbleBarDragZone(BubbleBarLocation)}
+     */
+    void onItemDraggedOutsideBubbleBarDropZone();
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index de6d1f6..52f2064 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -36,17 +36,21 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Log;
 import android.util.Size;
+import android.view.SurfaceControl;
 import android.widget.FrameLayout;
 
 import androidx.annotation.Nullable;
 
 import com.android.app.animation.Interpolators;
 import com.android.wm.shell.R;
+import com.android.wm.shell.animation.SizeChangeAnimation;
+import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.bubbles.BubbleOverflow;
 import com.android.wm.shell.bubbles.BubblePositioner;
 import com.android.wm.shell.bubbles.BubbleViewProvider;
@@ -571,6 +575,49 @@
     }
 
     /**
+     * Animates converting of a non-bubble task into an expanded bubble view.
+     */
+    public void animateConvert(BubbleViewProvider expandedBubble,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull Rect origBounds,
+            @NonNull SurfaceControl snapshot,
+            @NonNull SurfaceControl taskLeash,
+            @Nullable Runnable afterAnimation) {
+        mExpandedBubble = expandedBubble;
+        final BubbleBarExpandedView bbev = getExpandedView();
+        if (bbev == null) {
+            return;
+        }
+
+        bbev.setTaskViewAlpha(1f);
+        SurfaceControl tvSf = ((Bubble) mExpandedBubble).getTaskView().getSurfaceControl();
+
+        final Size size = getExpandedViewSize();
+        Point position = getExpandedViewRestPosition(size);
+
+        final SizeChangeAnimation sca =
+                new SizeChangeAnimation(
+                        new Rect(origBounds.left - position.x, origBounds.top - position.y,
+                                origBounds.right - position.x, origBounds.bottom - position.y),
+                        new Rect(0, 0, size.getWidth(), size.getHeight()));
+        sca.initialize(bbev, taskLeash, snapshot, startT);
+
+        Animator a = sca.buildViewAnimator(bbev, tvSf, snapshot, /* onFinish */ (va) -> {
+            updateExpandedView(bbev);
+            snapshot.release();
+            bbev.setSurfaceZOrderedOnTop(false);
+            bbev.setAnimating(false);
+            if (afterAnimation != null) {
+                afterAnimation.run();
+            }
+        });
+
+        bbev.setSurfaceZOrderedOnTop(true);
+        a.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION);
+        a.start();
+    }
+
+    /**
      * Cancel current animations
      */
     public void cancelAnimations() {
@@ -627,6 +674,13 @@
         bbev.maybeShowOverflow();
     }
 
+    void getExpandedViewRestBounds(Rect out) {
+        final int width = mPositioner.getExpandedViewWidthForBubbleBar(false /* overflow */);
+        final int height = mPositioner.getExpandedViewHeightForBubbleBar(false /* overflow */);
+        Point position = getExpandedViewRestPosition(new Size(width, height));
+        out.set(position.x, position.y, position.x + width, position.y + height);
+    }
+
     private Point getExpandedViewRestPosition(Size size) {
         final int padding = mPositioner.getBubbleBarExpandedViewPadding();
         Point point = new Point();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index eaa0bd2..f3f8d6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -28,6 +28,7 @@
 import android.graphics.Region;
 import android.graphics.drawable.ColorDrawable;
 import android.view.Gravity;
+import android.view.SurfaceControl;
 import android.view.TouchDelegate;
 import android.view.View;
 import android.view.ViewTreeObserver;
@@ -174,14 +175,34 @@
 
     /** Shows the expanded view of the provided bubble. */
     public void showExpandedView(BubbleViewProvider b) {
-        BubbleBarExpandedView expandedView = b.getBubbleBarExpandedView();
-        if (expandedView == null) {
-            return;
-        }
+        if (!canExpandView(b)) return;
+        animateExpand(prepareExpandedView(b));
+    }
+
+    /**
+     * @return whether it's possible to expand {@param b} right now. This is {@code false} if
+     *         the bubble has no view or if the bubble is already showing.
+     */
+    public boolean canExpandView(BubbleViewProvider b) {
+        if (b.getBubbleBarExpandedView() == null) return false;
         if (mExpandedBubble != null && mIsExpanded && b.getKey().equals(mExpandedBubble.getKey())) {
-            // Already showing this bubble, skip animating
-            return;
+            // Already showing this bubble so can't expand it.
+            return false;
         }
+        return true;
+    }
+
+    /**
+     * Prepares the expanded view of the provided bubble to be shown. This includes removing any
+     * stale content and cancelling any related animations.
+     *
+     * @return previous open bubble if there was one.
+     */
+    private BubbleViewProvider prepareExpandedView(BubbleViewProvider b) {
+        if (!canExpandView(b)) {
+            throw new IllegalStateException("Can't prepare expand. Check canExpandView(b) first.");
+        }
+        BubbleBarExpandedView expandedView = b.getBubbleBarExpandedView();
         BubbleViewProvider previousBubble = null;
         if (mExpandedBubble != null && !b.getKey().equals(mExpandedBubble.getKey())) {
             if (mIsExpanded && mExpandedBubble.getBubbleBarExpandedView() != null) {
@@ -251,7 +272,20 @@
 
         mIsExpanded = true;
         mBubbleController.getSysuiProxy().onStackExpandChanged(true);
+        showScrim(true);
+        return previousBubble;
+    }
 
+    /**
+     * Performs an animation to open a bubble with content that is not already visible.
+     *
+     * @param previousBubble If non-null, this is a bubble that is already showing before the new
+     *                       bubble is expanded.
+     */
+    public void animateExpand(BubbleViewProvider previousBubble) {
+        if (!mIsExpanded || mExpandedBubble == null) {
+            throw new IllegalStateException("Can't animateExpand without expnaded state");
+        }
         final Runnable afterAnimation = () -> {
             if (mExpandedView == null) return;
             // Touch delegate for the menu
@@ -274,14 +308,57 @@
         } else {
             mAnimationHelper.animateExpansion(mExpandedBubble, afterAnimation);
         }
+    }
 
-        showScrim(true);
+    /**
+     * Like {@link #prepareExpandedView} but also makes the current expanded bubble visible
+     * immediately so it gets a surface that can be animated. Since the surface may not be ready
+     * yet, this keeps the TaskView alpha=0.
+     */
+    public BubbleViewProvider prepareConvertedView(BubbleViewProvider b) {
+        final BubbleViewProvider prior = prepareExpandedView(b);
+
+        final BubbleBarExpandedView bbev = mExpandedBubble.getBubbleBarExpandedView();
+        if (bbev != null) {
+            updateExpandedView();
+            bbev.setAnimating(true);
+            bbev.setContentVisibility(true);
+            bbev.setSurfaceZOrderedOnTop(true);
+            bbev.setTaskViewAlpha(0.f);
+            bbev.setVisibility(VISIBLE);
+        }
+
+        return prior;
+    }
+
+    /**
+     * Starts and animates a conversion-from transition.
+     *
+     * @param startT A transaction with first-frame work. this *will* be applied here!
+     */
+    public void animateConvert(@NonNull SurfaceControl.Transaction startT,
+            @NonNull Rect startBounds, @NonNull SurfaceControl snapshot, SurfaceControl taskLeash,
+            Runnable animFinish) {
+        if (!mIsExpanded || mExpandedBubble == null) {
+            throw new IllegalStateException("Can't animateExpand without expanded state");
+        }
+        mAnimationHelper.animateConvert(mExpandedBubble, startT, startBounds, snapshot, taskLeash,
+                animFinish);
+    }
+
+    /**
+     * Populates {@param out} with the rest bounds of an expanded bubble.
+     */
+    public void getExpandedViewRestBounds(Rect out) {
+        mAnimationHelper.getExpandedViewRestBounds(out);
     }
 
     /** Removes the given {@code bubble}. */
     public void removeBubble(Bubble bubble, Runnable endAction) {
+        final boolean inTransition = bubble.getPreparingTransition() != null;
         Runnable cleanUp = () -> {
-            bubble.cleanupViews();
+            // The transition is already managing the task/wm state.
+            bubble.cleanupViews(!inTransition);
             endAction.run();
         };
         if (mBubbleData.getBubbles().isEmpty()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index e69d60d..4c3bde9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -39,6 +39,7 @@
 import com.android.window.flags.Flags;
 import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellInit;
 
 import java.util.ArrayList;
@@ -91,7 +92,8 @@
                 onDisplayAdded(displayIds[i]);
             }
 
-            if (Flags.enableConnectedDisplaysWindowDrag()) {
+            if (Flags.enableConnectedDisplaysWindowDrag()
+                    && DesktopModeStatus.canEnterDesktopMode(mContext)) {
                 mDisplayManager.registerTopologyListener(mMainExecutor,
                         this::onDisplayTopologyChanged);
                 onDisplayTopologyChanged(mDisplayManager.getDisplayTopology());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 94e629a..8377a35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -224,6 +224,12 @@
         }
     }
 
+    /** Hides the IME for Bubbles when the device is locked. */
+    public void hideImeForBubblesWhenLocked(int displayId) {
+        PerDisplay pd = mImePerDisplay.get(displayId);
+        pd.setImeInputTargetRequestedVisibility(false, pd.getImeSourceControl().getImeStatsToken());
+    }
+
     /** An implementation of {@link IDisplayWindowInsetsController} for a given display id. */
     public class PerDisplay implements DisplayInsetsController.OnInsetsChangedListener {
         final int mDisplayId;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
index 4cd2fd0..ff3e65a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
@@ -15,16 +15,21 @@
  */
 package com.android.wm.shell.common
 
+import android.annotation.UserIdInt
 import android.app.PendingIntent
 import android.content.ComponentName
 import android.content.Context
 import android.content.pm.LauncherApps
 import android.content.pm.PackageManager
+import android.content.pm.PackageManager.Property
 import android.os.UserHandle
 import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellInit
+import java.io.PrintWriter
 import java.util.Arrays
 
 /**
@@ -35,12 +40,23 @@
     private val packageManager: PackageManager,
     private val staticAppsSupportingMultiInstance: Array<String> = context.resources
             .getStringArray(R.array.config_appsSupportMultiInstancesSplit),
-    private val supportsMultiInstanceProperty: Boolean) {
+    shellInit: ShellInit,
+    private val shellCommandHandler: ShellCommandHandler,
+    private val supportsMultiInstanceProperty: Boolean
+) : ShellCommandHandler.ShellCommandActionHandler {
+
+    init {
+        shellInit.addInitCallback(this::onInit, this)
+    }
+
+    private fun onInit() {
+        shellCommandHandler.addCommandCallback("multi-instance", this, this)
+    }
 
     /**
      * Returns whether a specific component desires to be launched in multiple instances.
      */
-    fun supportsMultiInstanceSplit(componentName: ComponentName?): Boolean {
+    fun supportsMultiInstanceSplit(componentName: ComponentName?, @UserIdInt userId: Int): Boolean {
         if (componentName == null || componentName.packageName == null) {
             // TODO(b/262864589): Handle empty component case
             return false
@@ -63,8 +79,9 @@
 
         // Check the activity property first
         try {
-            val activityProp = packageManager.getProperty(
-                PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName)
+            val activityProp = packageManager.getPropertyAsUser(
+                PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName.packageName,
+                componentName.className, userId)
             // If the above call doesn't throw a NameNotFoundException, then the activity property
             // should override the application property value
             if (activityProp.isBoolean) {
@@ -80,8 +97,9 @@
 
         // Check the application property otherwise
         try {
-            val appProp = packageManager.getProperty(
-                PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName)
+            val appProp = packageManager.getPropertyAsUser(
+                PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, null /* className */,
+                userId)
             if (appProp.isBoolean) {
                 ProtoLog.v(WM_SHELL, "application=%s supports multi-instance", packageName)
                 return appProp.boolean
@@ -96,6 +114,66 @@
         return false
     }
 
+    override fun onShellCommand(args: Array<out String>?, pw: PrintWriter?): Boolean {
+        if (pw == null || args == null || args.isEmpty()) {
+            return false
+        }
+        when (args[0]) {
+            "list" -> return dumpSupportedApps(pw)
+        }
+        return false
+    }
+
+    override fun printShellCommandHelp(pw: PrintWriter, prefix: String) {
+        pw.println("${prefix}list")
+        pw.println("$prefix   Lists all the packages that support the multiinstance property")
+    }
+
+    /**
+     * Dumps the static allowlist and list of apps that have the declared property in the manifest.
+     */
+    private fun dumpSupportedApps(pw: PrintWriter): Boolean {
+        pw.println("Static allow list (for all users):")
+        staticAppsSupportingMultiInstance.forEach { pkg ->
+            pw.println("  $pkg")
+        }
+
+        // TODO(b/391693747): Dump this per-user once PM allows us to query properties
+        //                    for non-calling users
+        val apps = packageManager.queryApplicationProperty(
+            PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI)
+        val activities = packageManager.queryActivityProperty(
+            PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI)
+        val appsWithProperty = (apps + activities)
+            .sortedWith(object : Comparator<Property?> {
+                override fun compare(o1: Property?, o2: Property?): Int {
+                    if (o1?.packageName != o2?.packageName) {
+                        return o1?.packageName!!.compareTo(o2?.packageName!!)
+                    } else {
+                        if (o1?.className != null) {
+                            return o1.className!!.compareTo(o2?.className!!)
+                        } else if (o2?.className != null) {
+                            return -o2.className!!.compareTo(o1?.className!!)
+                        }
+                        return 0
+                    }
+                }
+            })
+        if (appsWithProperty.isNotEmpty()) {
+            pw.println("Apps (User ${context.userId}):")
+            appsWithProperty.forEach { prop ->
+                if (prop.isBoolean && prop.boolean) {
+                    if (prop.className != null) {
+                        pw.println("  ${prop.packageName}/${prop.className}")
+                    } else {
+                        pw.println("  ${prop.packageName}")
+                    }
+                }
+            }
+        }
+        return true
+    }
+
     companion object {
         /** Returns the component from a PendingIntent  */
         @JvmStatic
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/UserProfileContexts.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/UserProfileContexts.kt
index 0577f9e..1693864 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/UserProfileContexts.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/UserProfileContexts.kt
@@ -25,6 +25,7 @@
 import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.sysui.UserChangeListener
+import androidx.core.util.size
 
 /** Creates and manages contexts for all the profiles of the current user. */
 class UserProfileContexts(
@@ -35,6 +36,8 @@
     // Contexts for all the profiles of the current user.
     private val currentProfilesContext = SparseArray<Context>()
 
+    private val shellUserId = baseContext.userId
+
     lateinit var userContext: Context
         private set
 
@@ -49,6 +52,9 @@
                     currentProfilesContext.clear()
                     this@UserProfileContexts.userContext = userContext
                     currentProfilesContext.put(newUserId, userContext)
+                    if (newUserId != shellUserId) {
+                        currentProfilesContext.put(shellUserId, baseContext)
+                    }
                 }
 
                 override fun onUserProfilesChanged(profiles: List<UserInfo>) {
@@ -69,9 +75,9 @@
             currentProfilesContext.put(profile.id, profileContext)
         }
         val profilesToRemove = buildList<Int> {
-            for (i in 0..<currentProfilesContext.size()) {
+            for (i in 0..<currentProfilesContext.size) {
                 val userId = currentProfilesContext.keyAt(i)
-                if (profiles.none { it.id == userId }) {
+                if (userId != shellUserId && profiles.none { it.id == userId }) {
                     add(userId)
                 }
             }
@@ -80,4 +86,12 @@
     }
 
     operator fun get(userId: Int): Context? = currentProfilesContext.get(userId)
+
+    fun getOrCreate(userId: Int): Context {
+        val context = currentProfilesContext[userId]
+        if (context != null) return context
+        return baseContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0).also {
+            currentProfilesContext[userId] = it
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index cd5c135..bd89f5c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -394,11 +394,19 @@
      * Returns the divider position as a fraction from 0 to 1.
      */
     public float getDividerPositionAsFraction() {
-        return Math.min(1f, Math.max(0f, mIsLeftRightSplit
-                ? (float) ((getTopLeftBounds().right + getBottomRightBounds().left) / 2f)
-                        / getBottomRightBounds().right
-                : (float) ((getTopLeftBounds().bottom + getBottomRightBounds().top) / 2f)
-                        / getBottomRightBounds().bottom));
+        if (Flags.enableFlexibleTwoAppSplit()) {
+            return Math.min(1f, Math.max(0f, mIsLeftRightSplit
+                    ? (getTopLeftBounds().right + getBottomRightBounds().left) / 2f
+                    / getDisplayWidth()
+                    : (getTopLeftBounds().bottom + getBottomRightBounds().top) / 2f
+                            / getDisplayHeight()));
+        } else {
+            return Math.min(1f, Math.max(0f, mIsLeftRightSplit
+                    ? (float) ((getTopLeftBounds().right + getBottomRightBounds().left) / 2f)
+                    / getBottomRightBounds().right
+                    : (float) ((getTopLeftBounds().bottom + getBottomRightBounds().top) / 2f)
+                            / getBottomRightBounds().bottom));
+        }
     }
 
     private void updateInvisibleRect() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
deleted file mode 100644
index d1dcc9b1..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.
- */
-
-@file:JvmName("AppCompatUtils")
-
-package com.android.wm.shell.compatui
-
-import android.app.ActivityManager.RunningTaskInfo
-import android.content.Context
-import com.android.internal.R
-
-// TODO(b/347289970): Consider replacing with API
-/**
- * If the top activity should be exempt from desktop windowing and forced back to fullscreen.
- * Currently includes all system ui activities and modal dialogs. However if the top activity is not
- * being displayed, regardless of its configuration, we will not exempt it as to remain in the
- * desktop windowing environment.
- */
-fun isTopActivityExemptFromDesktopWindowing(context: Context, task: RunningTaskInfo) =
-    (isSystemUiTask(context, task) || isTransparentTask(task))
-            && !task.isTopActivityNoDisplay
-
-/**
- * Returns true if all activities in a tasks stack are transparent. If there are no activities will
- * return false.
- */
-fun isTransparentTask(task: RunningTaskInfo): Boolean = task.isActivityStackTransparent
-        && task.numActivities > 0
-
-private fun isSystemUiTask(context: Context, task: RunningTaskInfo): Boolean {
-    val sysUiPackageName: String =
-        context.resources.getString(R.string.config_systemUi)
-    return task.baseActivity?.packageName == sysUiPackageName
-}
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 1323fe0..201870f 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
@@ -37,9 +37,9 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.accessibility.AccessibilityManager;
+import android.window.DesktopModeFlags;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener;
@@ -71,7 +71,6 @@
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.IntPredicate;
 import java.util.function.Predicate;
 
 /**
@@ -874,6 +873,7 @@
         }
         boolean isDesktopModeShowing = mDesktopUserRepositories.get().getCurrent()
                 .getVisibleTaskCount(taskInfo.displayId) > 0;
-        return Flags.skipCompatUiEducationInDesktopMode() && isDesktopModeShowing;
+        return DesktopModeFlags.ENABLE_DESKTOP_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE_BUGFIX
+                .isTrue() && isDesktopModeShowing;
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 8404259..43f1a10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -31,6 +31,7 @@
 import android.provider.Settings;
 import android.view.IWindowManager;
 import android.view.accessibility.AccessibilityManager;
+import android.window.DesktopModeFlags;
 import android.window.SystemPerformanceHinter;
 
 import com.android.internal.logging.UiEventLogger;
@@ -43,6 +44,8 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
+import com.android.wm.shell.appzoomout.AppZoomOut;
+import com.android.wm.shell.appzoomout.AppZoomOutController;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.back.BackAnimationBackground;
 import com.android.wm.shell.back.BackAnimationController;
@@ -112,9 +115,8 @@
 import com.android.wm.shell.shared.annotations.ShellAnimationThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.shared.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
-import com.android.wm.shell.appzoomout.AppZoomOut;
-import com.android.wm.shell.appzoomout.AppZoomOutController;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.startingsurface.StartingSurface;
@@ -258,6 +260,12 @@
 
     @WMSingleton
     @Provides
+    static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(Context context) {
+        return new DesktopModeCompatPolicy(context);
+    }
+
+    @WMSingleton
+    @Provides
     static Optional<CompatUIHandler> provideCompatUIController(
             Context context,
             ShellInit shellInit,
@@ -308,7 +316,7 @@
     @WMSingleton
     @Provides
     static CompatUIStatusManager provideCompatUIStatusManager(@NonNull Context context) {
-        if (Flags.enableCompatUiVisibilityStatus()) {
+        if (DesktopModeFlags.ENABLE_DESKTOP_COMPAT_UI_VISIBILITY_STATUS.isTrue()) {
             return new CompatUIStatusManager(
                     newState -> Settings.Secure.putInt(context.getContentResolver(),
                             COMPAT_UI_EDUCATION_SHOWING, newState),
@@ -410,9 +418,13 @@
 
     @WMSingleton
     @Provides
-    static MultiInstanceHelper provideMultiInstanceHelper(Context context) {
+    static MultiInstanceHelper provideMultiInstanceHelper(
+            Context context,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler
+    ) {
         return new MultiInstanceHelper(context, context.getPackageManager(),
-                Flags.supportsMultiInstanceSystemUi());
+                shellInit, shellCommandHandler, Flags.supportsMultiInstanceSystemUi());
     }
 
     //
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index a058863..0f232d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.dagger;
 
-import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_SYSTEM_DIALOGS_TRANSITIONS;
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX;
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY;
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
@@ -37,6 +37,7 @@
 import android.view.Choreographer;
 import android.view.IWindowManager;
 import android.view.WindowManager;
+import android.window.DesktopModeFlags;
 
 import androidx.annotation.OptIn;
 
@@ -110,6 +111,7 @@
 import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository;
 import com.android.wm.shell.desktopmode.education.data.AppToWebEducationDatastoreRepository;
 import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer;
+import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver;
 import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer;
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository;
 import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer;
@@ -132,6 +134,7 @@
 import com.android.wm.shell.shared.annotations.ShellAnimationThread;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -398,6 +401,7 @@
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<DesktopUserRepositories> desktopUserRepositories,
             Optional<DesktopTasksController> desktopTasksController,
+            DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver,
             LaunchAdjacentController launchAdjacentController,
             WindowDecorViewModel windowDecorViewModel,
             Optional<TaskChangeListener> taskChangeListener) {
@@ -410,6 +414,7 @@
                 shellTaskOrganizer,
                 desktopUserRepositories,
                 desktopTasksController,
+                desktopModeLoggerTransitionObserver,
                 launchAdjacentController,
                 windowDecorViewModel,
                 taskChangeListener);
@@ -755,7 +760,10 @@
             DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
             Optional<BubbleController> bubbleController,
             OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver,
-            DesksOrganizer desksOrganizer) {
+            DesksOrganizer desksOrganizer,
+            DesksTransitionObserver desksTransitionObserver,
+            UserProfileContexts userProfileContexts,
+            DesktopModeCompatPolicy desktopModeCompatPolicy) {
         return new DesktopTasksController(
                 context,
                 shellInit,
@@ -790,7 +798,10 @@
                 desktopWallpaperActivityTokenProvider,
                 bubbleController,
                 overviewToDesktopTransitionObserver,
-                desksOrganizer);
+                desksOrganizer,
+                desksTransitionObserver,
+                userProfileContexts,
+                desktopModeCompatPolicy);
     }
 
     @WMSingleton
@@ -807,7 +818,9 @@
             ReturnToDragStartAnimator returnToDragStartAnimator,
             @DynamicOverride DesktopUserRepositories desktopUserRepositories,
             DesktopModeEventLogger desktopModeEventLogger,
-            WindowDecorTaskResourceLoader windowDecorTaskResourceLoader) {
+            WindowDecorTaskResourceLoader windowDecorTaskResourceLoader,
+            FocusTransitionObserver focusTransitionObserver,
+            @ShellMainThread ShellExecutor mainExecutor) {
         return new DesktopTilingDecorViewModel(
                 context,
                 mainDispatcher,
@@ -821,7 +834,9 @@
                 returnToDragStartAnimator,
                 desktopUserRepositories,
                 desktopModeEventLogger,
-                windowDecorTaskResourceLoader
+                windowDecorTaskResourceLoader,
+                focusTransitionObserver,
+                mainExecutor
         );
     }
 
@@ -899,9 +914,7 @@
             Transitions transitions,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             InteractionJankMonitor interactionJankMonitor) {
-        return (Flags.enableDesktopWindowingTransitions()
-                        || ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue()
-                        || ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX.isTrue())
+        return ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX.isTrue()
                 ? new SpringDragToDesktopTransitionHandler(
                         context, transitions, rootTaskDisplayAreaOrganizer, interactionJankMonitor)
                 : new DefaultDragToDesktopTransitionHandler(
@@ -922,7 +935,7 @@
         if (DesktopModeStatus.canEnterDesktopMode(context) && useKeyGestureEventHandler()
                 && manageKeyGestures()
                 && (Flags.enableMoveToNextDisplayShortcut()
-                || Flags.enableTaskResizingKeyboardShortcuts())) {
+                || DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue())) {
             return Optional.of(new DesktopModeKeyGestureHandler(context,
                     desktopModeWindowDecorViewModel, desktopTasksController,
                     inputManager, shellTaskOrganizer, focusTransitionObserver,
@@ -968,7 +981,8 @@
             DesktopModeEventLogger desktopModeEventLogger,
             DesktopModeUiEventLogger desktopModeUiEventLogger,
             WindowDecorTaskResourceLoader taskResourceLoader,
-            RecentsTransitionHandler recentsTransitionHandler
+            RecentsTransitionHandler recentsTransitionHandler,
+            DesktopModeCompatPolicy desktopModeCompatPolicy
     ) {
         if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
             return Optional.empty();
@@ -984,7 +998,7 @@
                 desktopTasksLimiter, appHandleEducationController, appToWebEducationController,
                 windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
                 focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger,
-                taskResourceLoader, recentsTransitionHandler));
+                taskResourceLoader, recentsTransitionHandler, desktopModeCompatPolicy));
     }
 
     @WMSingleton
@@ -1006,16 +1020,17 @@
             @ShellAnimationThread ShellExecutor animExecutor,
             ShellInit shellInit,
             Transitions transitions,
-            @DynamicOverride DesktopUserRepositories desktopUserRepositories) {
+            @DynamicOverride DesktopUserRepositories desktopUserRepositories,
+            DesktopModeCompatPolicy desktopModeCompatPolicy) {
         if (!DesktopModeStatus.canEnterDesktopMode(context)
                 || !ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
-                || !Flags.enableDesktopSystemDialogsTransitions()) {
+                || !ENABLE_DESKTOP_SYSTEM_DIALOGS_TRANSITIONS.isTrue()) {
             return Optional.empty();
         }
         return Optional.of(
                 new SystemModalsTransitionHandler(
                         context, mainExecutor, animExecutor, shellInit, transitions,
-                        desktopUserRepositories));
+                        desktopUserRepositories, desktopModeCompatPolicy));
     }
 
     @WMSingleton
@@ -1122,6 +1137,7 @@
             Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
             Optional<BackAnimationController> backAnimationController,
             DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
+            @NonNull DesksTransitionObserver desksTransitionObserver,
             ShellInit shellInit) {
         return desktopUserRepositories.flatMap(
                 repository ->
@@ -1134,11 +1150,20 @@
                                         desktopMixedTransitionHandler.get(),
                                         backAnimationController.get(),
                                         desktopWallpaperActivityTokenProvider,
+                                        desksTransitionObserver,
                                         shellInit)));
     }
 
     @WMSingleton
     @Provides
+    static DesksTransitionObserver provideDesksTransitionObserver(
+            @NonNull @DynamicOverride DesktopUserRepositories desktopUserRepositories
+    ) {
+        return new DesksTransitionObserver(desktopUserRepositories);
+    }
+
+    @WMSingleton
+    @Provides
     static Optional<DesktopMixedTransitionHandler> provideDesktopMixedTransitionHandler(
             Context context,
             Transitions transitions,
@@ -1201,7 +1226,8 @@
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             IWindowManager windowManager,
             Optional<DesktopUserRepositories> desktopUserRepositories,
-            Optional<DesktopTasksController> desktopTasksController
+            Optional<DesktopTasksController> desktopTasksController,
+            ShellTaskOrganizer shellTaskOrganizer
     ) {
         if (!DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.empty();
@@ -1215,7 +1241,8 @@
                         rootTaskDisplayAreaOrganizer,
                         windowManager,
                         desktopUserRepositories.get(),
-                        desktopTasksController.get()));
+                        desktopTasksController.get(),
+                        shellTaskOrganizer));
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 793bdf0..4133006 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -159,11 +159,12 @@
             PipUiEventLogger pipUiEventLogger,
             PipTaskListener pipTaskListener,
             @NonNull PipTransitionState pipTransitionState,
+            @NonNull PipDisplayLayoutState pipDisplayLayoutState,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler) {
         return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
-                systemWindows, pipUiEventLogger, pipTaskListener, pipTransitionState, mainExecutor,
-                mainHandler);
+                systemWindows, pipUiEventLogger, pipTaskListener, pipTransitionState,
+                pipDisplayLayoutState, mainExecutor, mainHandler);
     }
 
 
@@ -178,6 +179,8 @@
             @NonNull PipTransitionState pipTransitionState,
             @NonNull PipScheduler pipScheduler,
             @NonNull SizeSpecSource sizeSpecSource,
+            @NonNull PipDisplayLayoutState pipDisplayLayoutState,
+            DisplayController displayController,
             PipMotionHelper pipMotionHelper,
             FloatingContentCoordinator floatingContentCoordinator,
             PipUiEventLogger pipUiEventLogger,
@@ -185,8 +188,9 @@
             Optional<PipPerfHintController> pipPerfHintControllerOptional) {
         return new PipTouchHandler(context, shellInit, shellCommandHandler, menuPhoneController,
                 pipBoundsAlgorithm, pipBoundsState, pipTransitionState, pipScheduler,
-                sizeSpecSource, pipMotionHelper, floatingContentCoordinator, pipUiEventLogger,
-                mainExecutor, pipPerfHintControllerOptional);
+                sizeSpecSource, pipDisplayLayoutState, displayController, pipMotionHelper,
+                floatingContentCoordinator, pipUiEventLogger, mainExecutor,
+                pipPerfHintControllerOptional);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index 760d212..6f455df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -16,7 +16,10 @@
 
 package com.android.wm.shell.desktopmode
 
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.app.WindowConfiguration.windowingModeToString
 import android.content.Context
 import android.provider.Settings
 import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
@@ -27,6 +30,7 @@
 import com.android.internal.protolog.ProtoLog
 import com.android.window.flags.Flags
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
 import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
@@ -45,6 +49,7 @@
     private val windowManager: IWindowManager,
     private val desktopUserRepositories: DesktopUserRepositories,
     private val desktopTasksController: DesktopTasksController,
+    private val shellTaskOrganizer: ShellTaskOrganizer,
 ) : OnDisplaysChangedListener, OnDeskRemovedListener {
 
     private val desktopRepository: DesktopRepository
@@ -72,6 +77,11 @@
             return
         }
         logV("Creating new desk in new display#$displayId")
+        // TODO: b/362720497 - when SystemUI crashes with a freeform task open for any reason, the
+        //  task is recreated and received in [FreeformTaskListener] before this display callback
+        //  is invoked, which results in the repository trying to add the task to a desk before the
+        //  desk has been recreated here, which may result in a crash-loop if the repository is
+        //  checking that the desk exists before adding a task to it. See b/391984373.
         desktopTasksController.createDesk(displayId)
     }
 
@@ -119,13 +129,34 @@
             }
         val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)
         requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." }
-        if (tdaInfo.configuration.windowConfiguration.windowingMode == targetDisplayWindowingMode) {
+        val currentDisplayWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
+        if (currentDisplayWindowingMode == targetDisplayWindowingMode) {
             // Already in the target mode.
             return
         }
 
+        logV(
+            "As an external display is connected, changing default display's windowing mode from" +
+                " ${windowingModeToString(currentDisplayWindowingMode)}" +
+                " to ${windowingModeToString(targetDisplayWindowingMode)}"
+        )
+
         val wct = WindowContainerTransaction()
         wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode)
+        shellTaskOrganizer
+            .getRunningTasks(DEFAULT_DISPLAY)
+            .filter { it.activityType == ACTIVITY_TYPE_STANDARD }
+            .forEach {
+                // TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy
+                when (it.windowingMode) {
+                    currentDisplayWindowingMode -> {
+                        wct.setWindowingMode(it.token, currentDisplayWindowingMode)
+                    }
+                    targetDisplayWindowingMode -> {
+                        wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED)
+                    }
+                }
+            }
         transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
index 996d71c..9666ca9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
@@ -31,7 +31,6 @@
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
 import androidx.annotation.VisibleForTesting
-import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.protolog.ProtoLog
 import com.android.window.flags.Flags
@@ -83,10 +82,7 @@
 
     /** Starts close transition and handles or delegates desktop task close animation. */
     override fun startRemoveTransition(wct: WindowContainerTransaction?): IBinder {
-        if (
-            !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue &&
-                !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX.isTrue
-        ) {
+        if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX.isTrue) {
             return freeformTaskTransitionHandler.startRemoveTransition(wct)
         }
         requireNotNull(wct)
@@ -110,7 +106,6 @@
     ): IBinder {
         if (
             !Flags.enableFullyImmersiveInDesktop() &&
-                !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue &&
                 !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue
         ) {
             return transitions.startTransition(transitionType, wct, /* handler= */ null)
@@ -208,7 +203,6 @@
             return dispatchCloseLastDesktopTaskAnimation(
                 transition,
                 info,
-                closeChange,
                 startTransaction,
                 finishTransaction,
                 finishCallback,
@@ -259,10 +253,7 @@
             minimizeChange?.taskInfo?.taskId,
             immersiveExitChange?.taskInfo?.taskId,
         )
-        if (
-            DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue ||
-                DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue
-        ) {
+        if (DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue) {
             // Only apply minimize change reparenting here if we implement the new app launch
             // transitions, otherwise this reparenting is handled in the default handler.
             minimizeChange?.let {
@@ -352,18 +343,10 @@
     private fun dispatchCloseLastDesktopTaskAnimation(
         transition: IBinder,
         info: TransitionInfo,
-        change: TransitionInfo.Change,
         startTransaction: SurfaceControl.Transaction,
         finishTransaction: SurfaceControl.Transaction,
         finishCallback: TransitionFinishCallback,
     ): Boolean {
-        // Starting the jank trace if closing the last window in desktop mode.
-        interactionJankMonitor.begin(
-            change.leash,
-            context,
-            handler,
-            CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE,
-        )
         // Dispatch the last desktop task closing animation.
         return dispatchToLeftoverHandler(
             transition = transition,
@@ -371,10 +354,6 @@
             startTransaction = startTransaction,
             finishTransaction = finishTransaction,
             finishCallback = finishCallback,
-            doOnFinishCallback = {
-                // Finish the jank trace when closing the last window in desktop mode.
-                interactionJankMonitor.end(CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE)
-            },
         )
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 7897c0aa3..f5a95a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -24,10 +24,10 @@
 import android.view.MotionEvent.TOOL_TYPE_FINGER
 import android.view.MotionEvent.TOOL_TYPE_MOUSE
 import android.view.MotionEvent.TOOL_TYPE_STYLUS
+import android.window.DesktopModeFlags
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.protolog.ProtoLog
 import com.android.internal.util.FrameworkStatsLog
-import com.android.window.flags.Flags
 import com.android.wm.shell.EventLogTags
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -185,7 +185,7 @@
         displayController: DisplayController? = null,
         displayLayoutSize: Size? = null,
     ) {
-        if (!Flags.enableResizingMetrics()) return
+        if (!DesktopModeFlags.ENABLE_RESIZING_METRICS.isTrue) return
 
         val sessionId = currentSessionId.get()
         if (sessionId == NO_SESSION_ID) {
@@ -232,7 +232,7 @@
         displayController: DisplayController? = null,
         displayLayoutSize: Size? = null,
     ) {
-        if (!Flags.enableResizingMetrics()) return
+        if (!DesktopModeFlags.ENABLE_RESIZING_METRICS.isTrue) return
 
         val sessionId = currentSessionId.get()
         if (sessionId == NO_SESSION_ID) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
index 9334898..5269318 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
@@ -23,10 +23,10 @@
 import android.hardware.input.InputManager.KeyGestureEventHandler
 import android.hardware.input.KeyGestureEvent
 import android.os.IBinder
+import android.window.DesktopModeFlags
 import com.android.hardware.input.Flags.manageKeyGestures
 import com.android.internal.protolog.ProtoLog
 import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
-import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.ShellExecutor
@@ -144,7 +144,8 @@
             KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
             KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
             KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW ->
-                enableTaskResizingKeyboardShortcuts() && manageKeyGestures()
+                DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue &&
+                    manageKeyGestures()
             else -> false
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index a433586..3b05169 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -167,6 +167,29 @@
 
     override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {}
 
+    fun onTaskVanished(taskInfo: RunningTaskInfo) {
+        // At this point the task should have been cleared up due to transition. If it's not yet
+        // cleared up, it might be one of the edge cases where transitions don't give the correct
+        // signal.
+        if (visibleFreeformTaskInfos.containsKey(taskInfo.taskId)) {
+            val postTransitionFreeformTasks: SparseArray<TaskInfo> = SparseArray()
+            postTransitionFreeformTasks.putAll(visibleFreeformTaskInfos)
+            postTransitionFreeformTasks.remove(taskInfo.taskId)
+            ProtoLog.v(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: processing tasks after task vanished %s",
+                postTransitionFreeformTasks.size(),
+            )
+            identifyLogEventAndUpdateState(
+                transition = null,
+                transitionInfo = null,
+                preTransitionVisibleFreeformTasks = visibleFreeformTaskInfos,
+                postTransitionVisibleFreeformTasks = postTransitionFreeformTasks,
+                newFocusedFreeformTask = null,
+            )
+        }
+    }
+
     // Returns null if there was no change in focused task
     private fun getNewFocusedFreeformTask(info: TransitionInfo): TaskInfo? {
         val freeformWindowChanges =
@@ -253,8 +276,8 @@
      * state and update it
      */
     private fun identifyLogEventAndUpdateState(
-        transition: IBinder,
-        transitionInfo: TransitionInfo,
+        transition: IBinder?,
+        transitionInfo: TransitionInfo?,
         preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
         postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
         newFocusedFreeformTask: TaskInfo?,
@@ -310,8 +333,8 @@
 
     /** Compare the old and new state of taskInfos and identify and log the changes */
     private fun identifyAndLogTaskUpdates(
-        transition: IBinder,
-        transitionInfo: TransitionInfo,
+        transition: IBinder?,
+        transitionInfo: TransitionInfo?,
         preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
         postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
         newFocusedFreeformTask: TaskInfo?,
@@ -384,22 +407,24 @@
     }
 
     private fun getMinimizeReason(
-        transition: IBinder,
-        transitionInfo: TransitionInfo,
+        transition: IBinder?,
+        transitionInfo: TransitionInfo?,
         taskInfo: TaskInfo,
     ): MinimizeReason? {
-        if (transitionInfo.type == Transitions.TRANSIT_MINIMIZE) {
+        if (transitionInfo?.type == Transitions.TRANSIT_MINIMIZE) {
             return MinimizeReason.MINIMIZE_BUTTON
         }
-        val minimizingTask = desktopTasksLimiter.getOrNull()?.getMinimizingTask(transition)
+        val minimizingTask =
+            transition?.let { desktopTasksLimiter.getOrNull()?.getMinimizingTask(transition) }
         if (minimizingTask?.taskId == taskInfo.taskId) {
             return minimizingTask.minimizeReason
         }
         return null
     }
 
-    private fun getUnminimizeReason(transition: IBinder, taskInfo: TaskInfo): UnminimizeReason? {
-        val unminimizingTask = desktopTasksLimiter.getOrNull()?.getUnminimizingTask(transition)
+    private fun getUnminimizeReason(transition: IBinder?, taskInfo: TaskInfo): UnminimizeReason? {
+        val unminimizingTask =
+            transition?.let { desktopTasksLimiter.getOrNull()?.getUnminimizingTask(transition) }
         if (unminimizingTask?.taskId == taskInfo.taskId) {
             return unminimizingTask.unminimizeReason
         }
@@ -441,24 +466,24 @@
     }
 
     /** Get [EnterReason] for this session enter */
-    private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason {
+    private fun getEnterReason(transitionInfo: TransitionInfo?): EnterReason {
         val enterReason =
             when {
-                transitionInfo.type == WindowManager.TRANSIT_WAKE
+                transitionInfo?.type == WindowManager.TRANSIT_WAKE
                 // If there is a screen lock, desktop window entry is after dismissing keyguard
                 ||
-                    (transitionInfo.type == WindowManager.TRANSIT_TO_BACK &&
+                    (transitionInfo?.type == WindowManager.TRANSIT_TO_BACK &&
                         wasPreviousTransitionExitByScreenOff) -> EnterReason.SCREEN_ON
-                transitionInfo.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP ->
+                transitionInfo?.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP ->
                     EnterReason.APP_HANDLE_DRAG
-                transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON ->
+                transitionInfo?.type == TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON ->
                     EnterReason.APP_HANDLE_MENU_BUTTON
-                transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW ->
+                transitionInfo?.type == TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW ->
                     EnterReason.APP_FROM_OVERVIEW
-                transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT ->
+                transitionInfo?.type == TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT ->
                     EnterReason.KEYBOARD_SHORTCUT_ENTER
                 // NOTE: the below condition also applies for EnterReason quickswitch
-                transitionInfo.type == WindowManager.TRANSIT_TO_FRONT -> EnterReason.OVERVIEW
+                transitionInfo?.type == WindowManager.TRANSIT_TO_FRONT -> EnterReason.OVERVIEW
                 // Enter desktop mode from cancelled recents has no transition. Enter is detected on
                 // the
                 // next transition involving freeform windows.
@@ -469,12 +494,13 @@
                 // after
                 //  a cancelled recents.
                 wasPreviousTransitionExitToOverview -> EnterReason.OVERVIEW
-                transitionInfo.type == WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT
+                transitionInfo?.type == WindowManager.TRANSIT_OPEN ->
+                    EnterReason.APP_FREEFORM_INTENT
                 else -> {
                     ProtoLog.w(
                         WM_SHELL_DESKTOP_MODE,
                         "Unknown enter reason for transition type: %s",
-                        transitionInfo.type,
+                        transitionInfo?.type,
                     )
                     EnterReason.UNKNOWN_ENTER
                 }
@@ -484,30 +510,31 @@
     }
 
     /** Get [ExitReason] for this session exit */
-    private fun getExitReason(transitionInfo: TransitionInfo): ExitReason =
+    private fun getExitReason(transitionInfo: TransitionInfo?): ExitReason =
         when {
-            transitionInfo.type == WindowManager.TRANSIT_SLEEP -> {
+            transitionInfo?.type == WindowManager.TRANSIT_SLEEP -> {
                 wasPreviousTransitionExitByScreenOff = true
                 ExitReason.SCREEN_OFF
             }
             // TODO(b/384490301): differentiate back gesture / button exit from clicking the close
             // button located in the window top corner.
-            transitionInfo.type == WindowManager.TRANSIT_TO_BACK -> ExitReason.TASK_MOVED_TO_BACK
-            transitionInfo.type == WindowManager.TRANSIT_CLOSE -> ExitReason.TASK_FINISHED
-            transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -> ExitReason.DRAG_TO_EXIT
-            transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON ->
+            transitionInfo?.type == WindowManager.TRANSIT_TO_BACK -> ExitReason.TASK_MOVED_TO_BACK
+            transitionInfo?.type == WindowManager.TRANSIT_CLOSE -> ExitReason.TASK_FINISHED
+            transitionInfo?.type == TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -> ExitReason.DRAG_TO_EXIT
+            transitionInfo?.type == TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON ->
                 ExitReason.APP_HANDLE_MENU_BUTTON_EXIT
 
-            transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT ->
+            transitionInfo?.type == TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT ->
                 ExitReason.KEYBOARD_SHORTCUT_EXIT
 
-            transitionInfo.isExitToRecentsTransition() -> ExitReason.RETURN_HOME_OR_OVERVIEW
-            transitionInfo.type == Transitions.TRANSIT_MINIMIZE -> ExitReason.TASK_MINIMIZED
+            transitionInfo?.isExitToRecentsTransition() == true ->
+                ExitReason.RETURN_HOME_OR_OVERVIEW
+            transitionInfo?.type == Transitions.TRANSIT_MINIMIZE -> ExitReason.TASK_MINIMIZED
             else -> {
                 ProtoLog.w(
                     WM_SHELL_DESKTOP_MODE,
                     "Unknown exit reason for transition type: %s",
-                    transitionInfo.type,
+                    transitionInfo?.type,
                 )
                 ExitReason.UNKNOWN_EXIT
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
index 164d04b..b93d2e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
@@ -152,8 +152,8 @@
                 pw.println("Error: desk id should be an integer")
                 return false
             }
-        pw.println("Not implemented.")
-        return false
+        controller.removeDesk(deskId)
+        return true
     }
 
     private fun runRemoveAllDesks(args: Array<String>, pw: PrintWriter): Boolean {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 6636770..043b353 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -174,6 +174,9 @@
     /** Returns the number of desks in the given display. */
     fun getNumberOfDesks(displayId: Int) = desktopData.getNumberOfDesks(displayId)
 
+    /** Returns the display the given desk is in. */
+    fun getDisplayForDesk(deskId: Int) = desktopData.getDisplayForDesk(deskId)
+
     /** Adds [regionListener] to inform about changes to exclusion regions for all Desktop tasks. */
     fun setExclusionRegionListener(regionListener: Consumer<Region>, executor: Executor) {
         desktopGestureExclusionListener = regionListener
@@ -207,6 +210,14 @@
         desktopData.createDesk(displayId, deskId)
     }
 
+    /** Returns the ids of the existing desks in the given display. */
+    @VisibleForTesting
+    fun getDeskIds(displayId: Int): Set<Int> =
+        desktopData.desksSequence(displayId).map { desk -> desk.deskId }.toSet()
+
+    /** Returns the id of the default desk in the given display. */
+    fun getDefaultDeskId(displayId: Int): Int? = getDefaultDesk(displayId)?.deskId
+
     /** Returns the default desk in the given display. */
     private fun getDefaultDesk(displayId: Int): Desk? = desktopData.getDefaultDesk(displayId)
 
@@ -716,17 +727,13 @@
         }
     }
 
-    /**
-     * Removes the active desk for the given [displayId] and returns the active tasks on that desk.
-     *
-     * TODO: b/389960283 - add explicit [deskId] argument.
-     */
-    fun removeDesk(displayId: Int): ArraySet<Int> {
-        val desk = desktopData.getActiveDesk(displayId)
-        if (desk == null) {
-            logW("Could not find desk to remove: displayId=%d", displayId)
-            return ArraySet()
-        }
+    /** Removes the given desk and returns the active tasks in that desk. */
+    fun removeDesk(deskId: Int): Set<Int> {
+        val desk =
+            desktopData.getDesk(deskId)
+                ?: return emptySet<Int>().also {
+                    logW("Could not find desk to remove: deskId=%d", deskId)
+                }
         val activeTasks = ArraySet(desk.activeTasks)
         desktopData.remove(desk.deskId)
         return activeTasks
@@ -926,7 +933,10 @@
         }
 
         override fun getDesk(deskId: Int): Desk =
-            checkNotNull(deskByDisplayId[deskId]) { "Expected desk $deskId to exist" }
+            // TODO: b/362720497 - consider enforcing that the desk has been created before trying
+            //  to use it. As of now, there are cases where a task may be created faster than a
+            //  desk is, so just create it here if needed. See b/391984373.
+            deskByDisplayId.getOrCreate(deskId)
 
         override fun getActiveDesk(displayId: Int): Desk {
             // TODO: 389787966 - consider migrating to an "active" state instead of checking the
@@ -1063,7 +1073,7 @@
         }
 
         override fun getDisplayForDesk(deskId: Int): Int =
-            getAllActiveDesks().find { it.deskId == deskId }?.displayId
+            desksSequence().find { it.deskId == deskId }?.displayId
                 ?: error("Display for desk=$deskId not found")
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 6c57dc70..3f88e7b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.desktopmode
 
+import android.annotation.UserIdInt
 import android.app.ActivityManager
 import android.app.ActivityManager.RunningTaskInfo
 import android.app.ActivityOptions
@@ -55,6 +56,7 @@
 import android.widget.Toast
 import android.window.DesktopModeFlags
 import android.window.DesktopModeFlags.DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER
 import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
 import android.window.DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
 import android.window.RemoteTransition
@@ -85,8 +87,7 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SingleInstanceRemoteListener
 import com.android.wm.shell.common.SyncTransactionQueue
-import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
-import com.android.wm.shell.compatui.isTransparentTask
+import com.android.wm.shell.common.UserProfileContexts
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -102,7 +103,9 @@
 import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
 import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
+import com.android.wm.shell.desktopmode.multidesks.DeskTransition
 import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
+import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
 import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
 import com.android.wm.shell.draganddrop.DragAndDropController
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
@@ -115,6 +118,7 @@
 import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.shared.annotations.ExternalThread
 import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useDesktopOverrideDensity
@@ -183,6 +187,9 @@
     private val bubbleController: Optional<BubbleController>,
     private val overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver,
     private val desksOrganizer: DesksOrganizer,
+    private val desksTransitionObserver: DesksTransitionObserver,
+    private val userProfileContexts: UserProfileContexts,
+    private val desktopModeCompatPolicy: DesktopModeCompatPolicy,
 ) :
     RemoteCallable<DesktopTasksController>,
     Transitions.TransitionHandler,
@@ -514,10 +521,7 @@
         remoteTransition: RemoteTransition? = null,
         callback: IMoveToDesktopCallback? = null,
     ) {
-        if (
-            DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue() &&
-                isTopActivityExemptFromDesktopWindowing(context, task)
-        ) {
+        if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(task)) {
             logW("Cannot enter desktop for taskId %d, ineligible top activity found", task.taskId)
             return
         }
@@ -774,6 +778,12 @@
         val wct = WindowContainerTransaction()
         addMoveToFullscreenChanges(wct, task)
 
+        // We are moving a freeform task to fullscreen, put the home task under the fullscreen task.
+        if (!forceEnterDesktop(task.displayId)) {
+            moveHomeTask(wct, toTop = true, task.displayId)
+            wct.reorder(task.token, /* onTop= */ true)
+        }
+
         exitDesktopTaskTransitionHandler.startTransition(
             transitionSource,
             wct,
@@ -969,11 +979,13 @@
             cascadeWindow(bounds, displayLayout, displayId)
         }
         val pendingIntent =
-            PendingIntent.getActivity(
+            PendingIntent.getActivityAsUser(
                 context,
                 /* requestCode= */ 0,
                 intent,
                 PendingIntent.FLAG_IMMUTABLE,
+                /* options= */ null,
+                UserHandle.of(userId),
             )
         val ops =
             ActivityOptions.fromBundle(options).apply {
@@ -1486,6 +1498,7 @@
     }
 
     private fun addLaunchHomePendingIntent(wct: WindowContainerTransaction, displayId: Int) {
+        val userHandle = UserHandle.of(userId)
         val launchHomeIntent =
             Intent(Intent.ACTION_MAIN).apply {
                 if (displayId != DEFAULT_DISPLAY) {
@@ -1501,23 +1514,30 @@
                     ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
             }
         val pendingIntent =
-            PendingIntent.getActivity(
+            PendingIntent.getActivityAsUser(
                 context,
-                /* requestCode = */ 0,
+                /* requestCode= */ 0,
                 launchHomeIntent,
                 PendingIntent.FLAG_IMMUTABLE,
+                /* options= */ null,
+                userHandle,
             )
         wct.sendPendingIntent(pendingIntent, launchHomeIntent, options.toBundle())
     }
 
     private fun addWallpaperActivity(displayId: Int, wct: WindowContainerTransaction) {
         logV("addWallpaperActivity")
-        if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
+        if (ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER.isTrue()) {
+
+            // If the wallpaper activity for this display already exists, let's reorder it to top.
+            val wallpaperActivityToken = desktopWallpaperActivityTokenProvider.getToken(displayId)
+            if (wallpaperActivityToken != null) {
+                wct.reorder(wallpaperActivityToken, /* onTop= */ true)
+                return
+            }
+
             val intent = Intent(context, DesktopWallpaperActivity::class.java)
-            if (
-                desktopWallpaperActivityTokenProvider.getToken(displayId) == null &&
-                    Flags.enablePerDisplayDesktopWallpaperActivity()
-            ) {
+            if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
             }
@@ -1575,7 +1595,7 @@
     private fun removeWallpaperActivity(wct: WindowContainerTransaction, displayId: Int) {
         desktopWallpaperActivityTokenProvider.getToken(displayId)?.let { token ->
             logV("removeWallpaperActivity")
-            if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
+            if (ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER.isTrue()) {
                 wct.reorder(token, /* onTop= */ false)
             } else {
                 wct.removeTask(token)
@@ -1816,8 +1836,7 @@
             taskRepository.isActiveTask(triggerTask.taskId))
 
     private fun isIncompatibleTask(task: RunningTaskInfo) =
-        DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue() &&
-            isTopActivityExemptFromDesktopWindowing(context, task)
+        desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(task)
 
     private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean =
         ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue() &&
@@ -1859,7 +1878,10 @@
         //  need updates in some cases.
         val baseActivity = callingTaskInfo.baseActivity ?: return
         val fillIn: Intent =
-            context.packageManager.getLaunchIntentForPackage(baseActivity.packageName) ?: return
+            userProfileContexts
+                .getOrCreate(callingTaskInfo.userId)
+                .packageManager
+                .getLaunchIntentForPackage(baseActivity.packageName) ?: return
         fillIn.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
         val launchIntent =
             PendingIntent.getActivity(
@@ -2086,11 +2108,11 @@
      */
     private fun handleIncompatibleTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
         logV("handleIncompatibleTaskLaunch")
-        if (!isDesktopModeShowing(task.displayId)) return null
+        if (!isDesktopModeShowing(task.displayId) && !forceEnterDesktop(task.displayId)) return null
         // Only update task repository for transparent task.
         if (
             DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
-                .isTrue() && isTransparentTask(task)
+                .isTrue() && desktopModeCompatPolicy.isTransparentTask(task)
         ) {
             taskRepository.setTopTransparentFullscreenTaskId(task.displayId, task.taskId)
         }
@@ -2341,10 +2363,7 @@
         launchTaskId: Int,
         minimizeTaskId: Int?,
     ) {
-        if (
-            !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue &&
-                !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue
-        ) {
+        if (!DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue) {
             return
         }
         // TODO b/359523924: pass immersive task here?
@@ -2358,20 +2377,62 @@
         )
     }
 
-    fun removeDesktop(displayId: Int) {
-        if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
-
-        val tasksToRemove = taskRepository.removeDesk(displayId)
-        val wct = WindowContainerTransaction()
-        tasksToRemove.forEach {
-            val task = shellTaskOrganizer.getRunningTaskInfo(it)
-            if (task != null) {
-                wct.removeTask(task.token)
-            } else {
-                recentTasksController?.removeBackgroundTask(it)
+    /** Removes the default desk in the given display. */
+    @Deprecated("Deprecated with multi-desks.", ReplaceWith("removeDesk()"))
+    fun removeDefaultDeskInDisplay(displayId: Int) {
+        val deskId =
+            checkNotNull(taskRepository.getDefaultDeskId(displayId)) {
+                "Expected a default desk to exist"
             }
+        removeDesk(displayId = displayId, deskId = deskId)
+    }
+
+    /** Removes the given desk. */
+    fun removeDesk(deskId: Int) {
+        val displayId = taskRepository.getDisplayForDesk(deskId)
+        removeDesk(displayId = displayId, deskId = deskId)
+    }
+
+    private fun removeDesk(displayId: Int, deskId: Int) {
+        if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
+        logV("removeDesk deskId=%d from displayId=%d", deskId, displayId)
+
+        val tasksToRemove =
+            if (Flags.enableMultipleDesktopsBackend()) {
+                taskRepository.getActiveTaskIdsInDesk(deskId)
+            } else {
+                // TODO: 362720497 - make sure minimized windows are also removed in WM
+                //  and the repository.
+                taskRepository.removeDesk(deskId)
+            }
+
+        val wct = WindowContainerTransaction()
+        if (!Flags.enableMultipleDesktopsBackend()) {
+            tasksToRemove.forEach {
+                val task = shellTaskOrganizer.getRunningTaskInfo(it)
+                if (task != null) {
+                    wct.removeTask(task.token)
+                } else {
+                    recentTasksController?.removeBackgroundTask(it)
+                }
+            }
+        } else {
+            // TODO: 362720497 - double check background tasks are also removed.
+            desksOrganizer.removeDesk(wct, deskId)
         }
-        if (!wct.isEmpty) transitions.startTransition(TRANSIT_CLOSE, wct, null)
+        if (!Flags.enableMultipleDesktopsBackend() && wct.isEmpty) return
+        val transition = transitions.startTransition(TRANSIT_CLOSE, wct, /* handler= */ null)
+        if (Flags.enableMultipleDesktopsBackend()) {
+            desksTransitionObserver.addPendingTransition(
+                DeskTransition.RemoveDesk(
+                    token = transition,
+                    displayId = displayId,
+                    deskId = deskId,
+                    tasks = tasksToRemove,
+                    onDeskRemovedListener = onDeskRemovedListener,
+                )
+            )
+        }
     }
 
     /** Enter split by using the focused desktop task in given `displayId`. */
@@ -2501,7 +2562,6 @@
      *
      * @param taskInfo the task being dragged.
      * @param taskSurface the leash of the task being dragged.
-     * @param position position of surface when drag ends.
      * @param inputCoordinate the coordinates of the motion event
      * @param currentDragBounds the current bounds of where the visible task is (might be actual
      *   task bounds or just task leash)
@@ -2511,7 +2571,6 @@
     fun onDragPositioningEnd(
         taskInfo: RunningTaskInfo,
         taskSurface: SurfaceControl,
-        position: Point,
         inputCoordinate: PointF,
         currentDragBounds: Rect,
         validDragArea: Rect,
@@ -2539,7 +2598,7 @@
                     )
                     moveToFullscreenWithAnimation(
                         taskInfo,
-                        position,
+                        Point(currentDragBounds.left, currentDragBounds.top),
                         DesktopModeTransitionSource.TASK_DRAG,
                     )
                 }
@@ -2738,6 +2797,7 @@
     // TODO(b/358114479): Move this implementation into a separate class.
     override fun onUnhandledDrag(
         launchIntent: PendingIntent,
+        @UserIdInt userId: Int,
         dragEvent: DragEvent,
         onFinishCallback: Consumer<Boolean>,
     ): Boolean {
@@ -2746,8 +2806,10 @@
             // Not currently in desktop mode, ignore the drop
             return false
         }
+
+        // TODO:
         val launchComponent = getComponent(launchIntent)
-        if (!multiInstanceHelper.supportsMultiInstanceSplit(launchComponent)) {
+        if (!multiInstanceHelper.supportsMultiInstanceSplit(launchComponent, userId)) {
             // TODO(b/320797628): Should only return early if there is an existing running task, and
             //                    notify the user as well. But for now, just ignore the drop.
             logV("Dropped intent does not support multi-instance")
@@ -3007,6 +3069,14 @@
             controller = null
         }
 
+        override fun createDesk(displayId: Int) {
+            // TODO: b/362720497 - Implement this API.
+        }
+
+        override fun activateDesk(deskId: Int, remoteTransition: RemoteTransition?) {
+            // TODO: b/362720497 - Implement this API.
+        }
+
         override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) {
             executeRemoteCallWithTaskPermission(controller, "showDesktopApps") { c ->
                 c.showDesktopApps(displayId, remoteTransition)
@@ -3034,17 +3104,6 @@
             )
         }
 
-        override fun getVisibleTaskCount(displayId: Int): Int {
-            val result = IntArray(1)
-            executeRemoteCallWithTaskPermission(
-                controller,
-                "visibleTaskCount",
-                { controller -> result[0] = controller.visibleTaskCount(displayId) },
-                /* blocking= */ true,
-            )
-            return result[0]
-        }
-
         override fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo) {
             executeRemoteCallWithTaskPermission(controller, "onDesktopSplitSelectAnimComplete") { c
                 ->
@@ -3077,7 +3136,7 @@
 
         override fun removeDesktop(displayId: Int) {
             executeRemoteCallWithTaskPermission(controller, "removeDesktop") { c ->
-                c.removeDesktop(displayId)
+                c.removeDefaultDeskInDisplay(displayId)
             }
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index 14c8429..3ada988 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -27,6 +27,7 @@
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.DesktopModeFlags
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER
 import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
@@ -36,6 +37,7 @@
 import com.android.wm.shell.back.BackAnimationController
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -57,6 +59,7 @@
     private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
     private val backAnimationController: BackAnimationController,
     private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
+    private val desksTransitionObserver: DesksTransitionObserver,
     shellInit: ShellInit,
 ) : Transitions.TransitionObserver {
 
@@ -86,6 +89,7 @@
         finishTransaction: SurfaceControl.Transaction,
     ) {
         // TODO: b/332682201 Update repository state
+        desksTransitionObserver.onTransitionReady(transition, info)
         if (
             DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
                 .isTrue() && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
@@ -275,7 +279,7 @@
             desktopWallpaperActivityTokenProvider
                 .getToken(lastSeenTransitionToCloseWallpaper.displayId)
                 ?.let { wallpaperActivityToken ->
-                    if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
+                    if (ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER.isTrue()) {
                         transitions.startTransition(
                             TRANSIT_TO_BACK,
                             WindowContainerTransaction()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
index 13576aa..a5ba661 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
@@ -21,9 +21,9 @@
 import android.content.pm.UserInfo
 import android.os.UserManager
 import android.util.SparseArray
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_HSUM
 import androidx.core.util.forEach
 import com.android.internal.protolog.ProtoLog
-import com.android.window.flags.Flags
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
 import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -68,7 +68,7 @@
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             shellInit.addInitCallback(::onInit, this)
         }
-        if (Flags.enableDesktopWindowingHsum()) {
+        if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
             userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
         }
     }
@@ -80,7 +80,7 @@
 
     /** Returns [DesktopRepository] for the parent user id. */
     fun getProfile(profileId: Int): DesktopRepository {
-        if (Flags.enableDesktopWindowingHsum()) {
+        if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
             for ((uid, profileIds) in userIdToProfileIdsMap) {
                 if (profileId in profileIds) {
                     return desktopRepoByUserId.getOrCreate(uid)
@@ -101,14 +101,14 @@
     override fun onUserChanged(newUserId: Int, userContext: Context) {
         logD("onUserChanged previousUserId=%d, newUserId=%d", userId, newUserId)
         userId = newUserId
-        if (Flags.enableDesktopWindowingHsum()) {
+        if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
             sanitizeUsers()
         }
     }
 
     override fun onUserProfilesChanged(profiles: MutableList<UserInfo>) {
         logD("onUserProfilesChanged profiles=%s", profiles.toString())
-        if (Flags.enableDesktopWindowingHsum()) {
+        if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
             // TODO(b/366397912): Remove all persisted profile data when the profile changes.
             userIdToProfileIdsMap[userId] = profiles.map { it.id }
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 91f10dc..cc3d86c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -458,7 +458,8 @@
     override fun mergeAnimation(
         transition: IBinder,
         info: TransitionInfo,
-        t: SurfaceControl.Transaction,
+        startT: SurfaceControl.Transaction,
+        finishT: SurfaceControl.Transaction,
         mergeTarget: IBinder,
         finishCallback: Transitions.TransitionFinishCallback,
     ) {
@@ -488,18 +489,18 @@
         if (isEndTransition) {
             setupEndDragToDesktop(
                 info,
-                startTransaction = t,
+                startTransaction = startT,
                 finishTransaction = startTransactionFinishT,
             )
             // Call finishCallback to merge animation before startTransitionFinishCb is called
             finishCallback.onTransitionFinished(/* wct= */ null)
-            animateEndDragToDesktop(startTransaction = t, startTransitionFinishCb)
+            animateEndDragToDesktop(startTransaction = startT, startTransitionFinishCb)
         } else if (isCancelTransition) {
             info.changes.forEach { change ->
-                t.show(change.leash)
+                startT.show(change.leash)
                 startTransactionFinishT.show(change.leash)
             }
-            t.apply()
+            startT.apply()
             finishCallback.onTransitionFinished(/* wct= */ null)
             startTransitionFinishCb.onTransitionFinished(/* wct= */ null)
             clearState()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
index b902bb4..f7f87ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -32,6 +32,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.util.DisplayMetrics;
+import android.view.Choreographer;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.WindowManager.TransitionType;
@@ -184,6 +185,7 @@
                 t.setPosition(sc, mPosition.x * (1 - fraction), mPosition.y * (1 - fraction))
                         .setScale(sc, currentScaleX, currentScaleY)
                         .show(sc)
+                        .setFrameTimeline(Choreographer.getInstance().getVsyncId())
                         .apply();
             });
             animator.addListener(new AnimatorListenerAdapter() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index a135e44..44f7e16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -29,6 +29,11 @@
  * Interface that is exposed to remote callers to manipulate desktop mode features.
  */
 interface IDesktopMode {
+    /** If possible, creates a new desk on the display whose ID is `displayId`. */
+    oneway void createDesk(int displayId);
+
+    /** Activates the desk whose ID is `deskId` on whatever display it currently exists on. */
+    oneway void activateDesk(int deskId, in RemoteTransition remoteTransition);
 
     /** Show apps on the desktop on the given display */
     void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
@@ -48,9 +53,6 @@
     oneway void showDesktopApp(int taskId, in @nullable RemoteTransition remoteTransition,
             in DesktopTaskToFrontReason toFrontReason);
 
-    /** Get count of visible desktop tasks on the given display */
-    int getVisibleTaskCount(int displayId);
-
     /** Perform cleanup transactions after the animation to split select is complete */
     oneway void onDesktopSplitSelectAnimComplete(in RunningTaskInfo taskInfo);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
index a428ce1..224ff37 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.desktopmode.compatui
 
 import android.animation.ValueAnimator
+import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
 import android.os.IBinder
 import android.view.Display.DEFAULT_DISPLAY
@@ -28,13 +29,14 @@
 import com.android.app.animation.Interpolators
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.desktopmode.DesktopWallpaperActivity
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.TransitionUtil.isClosingMode
 import com.android.wm.shell.shared.TransitionUtil.isClosingType
 import com.android.wm.shell.shared.TransitionUtil.isOpeningMode
 import com.android.wm.shell.shared.TransitionUtil.isOpeningType
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.TransitionHandler
@@ -47,6 +49,7 @@
     private val shellInit: ShellInit,
     private val transitions: Transitions,
     private val desktopUserRepositories: DesktopUserRepositories,
+    private val desktopModeCompatPolicy: DesktopModeCompatPolicy,
 ) : TransitionHandler {
 
     private val showingSystemModalsIds = mutableSetOf<Int>()
@@ -128,7 +131,7 @@
                 return@find false
             }
             val taskInfo = change.taskInfo ?: return@find false
-            return@find isTopActivityExemptFromDesktopWindowing(context, taskInfo)
+            return@find isSystemModal(taskInfo)
         }
 
     private fun getClosingSystemModal(info: TransitionInfo): TransitionInfo.Change? =
@@ -137,10 +140,13 @@
                 return@find false
             }
             val taskInfo = change.taskInfo ?: return@find false
-            return@find isTopActivityExemptFromDesktopWindowing(context, taskInfo) ||
-                showingSystemModalsIds.contains(taskInfo.taskId)
+            return@find isSystemModal(taskInfo) || showingSystemModalsIds.contains(taskInfo.taskId)
         }
 
+    private fun isSystemModal(taskInfo: RunningTaskInfo): Boolean =
+        !DesktopWallpaperActivity.isWallpaperTask(taskInfo) &&
+            desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)
+
     private fun createAlphaAnimator(
         transaction: SurfaceControl.Transaction,
         leash: SurfaceControl,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
index 5757c6af..5d83556 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
@@ -22,7 +22,7 @@
 import android.content.res.Resources
 import android.graphics.Point
 import android.os.SystemProperties
-import android.util.Slog
+import android.view.View.LAYOUT_DIRECTION_RTL
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
 import com.android.wm.shell.desktopmode.CaptionState
@@ -32,27 +32,18 @@
 import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
-import com.android.wm.shell.windowdecor.common.DecorThemeUtil
 import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController
 import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipColorScheme
 import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipEducationViewConfig
-import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.MainCoroutineDispatcher
-import kotlinx.coroutines.TimeoutCancellationException
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.debounce
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.take
-import kotlinx.coroutines.flow.timeout
 import kotlinx.coroutines.launch
 
 /**
@@ -72,61 +63,104 @@
     @ShellMainThread private val applicationCoroutineScope: CoroutineScope,
     @ShellBackgroundThread private val backgroundDispatcher: MainCoroutineDispatcher,
 ) {
-    private val decorThemeUtil = DecorThemeUtil(context)
     private lateinit var openHandleMenuCallback: (Int) -> Unit
     private lateinit var toDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit
+    private val onTertiaryFixedColor =
+        context.getColor(com.android.internal.R.color.materialColorOnTertiaryFixed)
+    private val tertiaryFixedColor =
+        context.getColor(com.android.internal.R.color.materialColorTertiaryFixed)
 
     init {
         runIfEducationFeatureEnabled {
+            // Coroutine block for the first hint that appears on a full-screen app's app handle to
+            // encourage users to open the app handle menu.
             applicationCoroutineScope.launch {
-                // Central block handling the app handle's educational flow end-to-end.
-                isAppHandleHintViewedFlow()
-                    .flatMapLatest { isAppHandleHintViewed ->
-                        if (isAppHandleHintViewed) {
-                            // If the education is viewed then return emptyFlow() that completes
-                            // immediately.
-                            // This will help us to not listen to [captionHandleStateFlow] after the
-                            // education
-                            // has been viewed already.
-                            emptyFlow()
-                        } else {
-                            // Listen for changes to window decor's caption handle.
-                            windowDecorCaptionHandleRepository.captionStateFlow
-                                // Wait for few seconds before emitting the latest state.
-                                .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
-                                .filter { captionState ->
-                                    captionState is CaptionState.AppHandle &&
-                                        appHandleEducationFilter.shouldShowAppHandleEducation(
-                                            captionState
-                                        )
-                                }
-                        }
-                    }
-                    .flowOn(backgroundDispatcher)
-                    .collectLatest { captionState ->
-                        val tooltipColorScheme = tooltipColorScheme(captionState)
-
-                        showEducation(captionState, tooltipColorScheme)
-                        // After showing first tooltip, mark education as viewed
-                        appHandleEducationDatastoreRepository
-                            .updateAppHandleHintViewedTimestampMillis(true)
-                    }
-            }
-
-            applicationCoroutineScope.launch {
-                if (isAppHandleHintUsed()) return@launch
+                if (isAppHandleHintViewed()) return@launch
                 windowDecorCaptionHandleRepository.captionStateFlow
+                    .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
                     .filter { captionState ->
-                        captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded
+                        captionState is CaptionState.AppHandle &&
+                            !captionState.isHandleMenuExpanded &&
+                            !isAppHandleHintViewed() &&
+                            appHandleEducationFilter.shouldShowDesktopModeEducation(captionState)
                     }
                     .take(1)
                     .flowOn(backgroundDispatcher)
-                    .collect {
-                        // If user expands app handle, mark user has used the app handle hint
+                    .collectLatest { captionState ->
+                        showEducation(captionState)
                         appHandleEducationDatastoreRepository
-                            .updateAppHandleHintUsedTimestampMillis(true)
+                            .updateAppHandleHintViewedTimestampMillis(true)
+                        delay(TOOLTIP_VISIBLE_DURATION_MILLIS)
+                        windowingEducationViewController.hideEducationTooltip()
                     }
             }
+
+            // Coroutine block for the hint that appears when an app handle is expanded to
+            // encourage users to enter desktop mode.
+            applicationCoroutineScope.launch {
+                if (isEnterDesktopModeHintViewed()) return@launch
+                windowDecorCaptionHandleRepository.captionStateFlow
+                    .debounce(ENTER_DESKTOP_MODE_EDUCATION_DELAY_MILLIS)
+                    .filter { captionState ->
+                        captionState is CaptionState.AppHandle &&
+                            captionState.isHandleMenuExpanded &&
+                            !isEnterDesktopModeHintViewed() &&
+                            appHandleEducationFilter.shouldShowDesktopModeEducation(captionState)
+                    }
+                    .take(1)
+                    .flowOn(backgroundDispatcher)
+                    .collectLatest { captionState ->
+                        showWindowingImageButtonTooltip(captionState as CaptionState.AppHandle)
+                        appHandleEducationDatastoreRepository
+                            .updateEnterDesktopModeHintViewedTimestampMillis(true)
+                        delay(TOOLTIP_VISIBLE_DURATION_MILLIS)
+                        windowingEducationViewController.hideEducationTooltip()
+                    }
+            }
+
+            // Coroutine block for the hint that appears on the window app header in freeform mode
+            // to let users know how to exit desktop mode.
+            applicationCoroutineScope.launch {
+                if (isExitDesktopModeHintViewed()) return@launch
+                windowDecorCaptionHandleRepository.captionStateFlow
+                    .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
+                    .filter { captionState ->
+                        captionState is CaptionState.AppHeader &&
+                            !captionState.isHeaderMenuExpanded &&
+                            !isExitDesktopModeHintViewed() &&
+                            appHandleEducationFilter.shouldShowDesktopModeEducation(captionState)
+                    }
+                    .take(1)
+                    .flowOn(backgroundDispatcher)
+                    .collectLatest { captionState ->
+                        showExitWindowingTooltip(captionState as CaptionState.AppHeader)
+                        appHandleEducationDatastoreRepository
+                            .updateExitDesktopModeHintViewedTimestampMillis(true)
+                        delay(TOOLTIP_VISIBLE_DURATION_MILLIS)
+                        windowingEducationViewController.hideEducationTooltip()
+                    }
+            }
+
+            // Listens to a [NoCaption] state change to dismiss any tooltip if the app handle or app
+            // header is gone or de-focused (e.g. when a user swipes up to home, overview, or enters
+            // split screen)
+            applicationCoroutineScope.launch {
+                if (
+                    isAppHandleHintViewed() &&
+                        isEnterDesktopModeHintViewed() &&
+                        isExitDesktopModeHintViewed()
+                )
+                    return@launch
+                windowDecorCaptionHandleRepository.captionStateFlow
+                    .filter { captionState ->
+                        captionState is CaptionState.NoCaption &&
+                            !isAppHandleHintViewed() &&
+                            !isEnterDesktopModeHintViewed() &&
+                            !isExitDesktopModeHintViewed()
+                    }
+                    .flowOn(backgroundDispatcher)
+                    .collectLatest { windowingEducationViewController.hideEducationTooltip() }
+            }
         }
     }
 
@@ -135,31 +169,29 @@
             block()
     }
 
-    private fun showEducation(captionState: CaptionState, tooltipColorScheme: TooltipColorScheme) {
+    private fun showEducation(captionState: CaptionState) {
         val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds
         val tooltipGlobalCoordinates =
             Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom)
-        // TODO: b/370546801 - Differentiate between user dismissing the tooltip vs following the
-        // cue.
         // Populate information important to inflate app handle education tooltip.
         val appHandleTooltipConfig =
             TooltipEducationViewConfig(
                 tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip,
-                tooltipColorScheme = tooltipColorScheme,
+                tooltipColorScheme =
+                    TooltipColorScheme(
+                        tertiaryFixedColor,
+                        onTertiaryFixedColor,
+                        onTertiaryFixedColor,
+                    ),
                 tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
                 tooltipText = getString(R.string.windowing_app_handle_education_tooltip),
                 arrowDirection =
                     DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP,
                 onEducationClickAction = {
-                    launchWithExceptionHandling {
-                        showWindowingImageButtonTooltip(tooltipColorScheme)
-                    }
                     openHandleMenuCallback(captionState.runningTaskInfo.taskId)
                 },
                 onDismissAction = {
-                    launchWithExceptionHandling {
-                        showWindowingImageButtonTooltip(tooltipColorScheme)
-                    }
+                    // TODO: b/341320146 - Log previous tooltip was dismissed
                 },
             )
 
@@ -170,7 +202,7 @@
     }
 
     /** Show tooltip that points to windowing image button in app handle menu */
-    private suspend fun showWindowingImageButtonTooltip(tooltipColorScheme: TooltipColorScheme) {
+    private suspend fun showWindowingImageButtonTooltip(captionState: CaptionState.AppHandle) {
         val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height)
         val windowingOptionPillHeight =
             getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height)
@@ -181,128 +213,90 @@
             getSize(R.dimen.desktop_mode_handle_menu_margin_top) +
                 getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin)
 
-        windowDecorCaptionHandleRepository.captionStateFlow
-            // After the first tooltip was dismissed, wait for 400 ms and see if the app handle menu
-            // has been expanded.
-            .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds)
-            .catchTimeoutAndLog {
-                // TODO: b/341320146 - Log previous tooltip was dismissed
-            }
-            // Wait for few milliseconds before emitting the latest state.
-            .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
-            .filter { captionState ->
-                // Filter out states when app handle is not visible or not expanded.
-                captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded
-            }
-            // Before showing this tooltip, stop listening to further emissions to avoid
-            // accidentally
-            // showing the same tooltip on future emissions.
-            .take(1)
-            .flowOn(backgroundDispatcher)
-            .collectLatest { captionState ->
-                captionState as CaptionState.AppHandle
-                val appHandleBounds = captionState.globalAppHandleBounds
-                val tooltipGlobalCoordinates =
-                    Point(
-                        appHandleBounds.left + appHandleBounds.width() / 2 + appHandleMenuWidth / 2,
-                        appHandleBounds.top +
-                            appHandleMenuMargins +
-                            appInfoPillHeight +
-                            windowingOptionPillHeight / 2,
+        val appHandleBounds = captionState.globalAppHandleBounds
+        val appHandleCenterX = appHandleBounds.left + appHandleBounds.width() / 2
+        val tooltipGlobalCoordinates =
+            Point(
+                if (isRtl()) {
+                    appHandleCenterX - appHandleMenuWidth / 2
+                } else {
+                    appHandleCenterX + appHandleMenuWidth / 2
+                },
+                appHandleBounds.top +
+                    appHandleMenuMargins +
+                    appInfoPillHeight +
+                    windowingOptionPillHeight / 2,
+            )
+        // Populate information important to inflate windowing image button education
+        // tooltip.
+        val windowingImageButtonTooltipConfig =
+            TooltipEducationViewConfig(
+                tooltipViewLayout = R.layout.desktop_windowing_education_horizontal_arrow_tooltip,
+                tooltipColorScheme =
+                    TooltipColorScheme(
+                        tertiaryFixedColor,
+                        onTertiaryFixedColor,
+                        onTertiaryFixedColor,
+                    ),
+                tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
+                tooltipText =
+                    getString(R.string.windowing_desktop_mode_image_button_education_tooltip),
+                arrowDirection =
+                    DesktopWindowingEducationTooltipController.TooltipArrowDirection.HORIZONTAL,
+                onEducationClickAction = {
+                    toDesktopModeCallback(
+                        captionState.runningTaskInfo.taskId,
+                        DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON,
                     )
-                // Populate information important to inflate windowing image button education
-                // tooltip.
-                val windowingImageButtonTooltipConfig =
-                    TooltipEducationViewConfig(
-                        tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip,
-                        tooltipColorScheme = tooltipColorScheme,
-                        tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
-                        tooltipText =
-                            getString(
-                                R.string.windowing_desktop_mode_image_button_education_tooltip
-                            ),
-                        arrowDirection =
-                            DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT,
-                        onEducationClickAction = {
-                            launchWithExceptionHandling {
-                                showExitWindowingTooltip(tooltipColorScheme)
-                            }
-                            toDesktopModeCallback(
-                                captionState.runningTaskInfo.taskId,
-                                DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON,
-                            )
-                        },
-                        onDismissAction = {
-                            launchWithExceptionHandling {
-                                showExitWindowingTooltip(tooltipColorScheme)
-                            }
-                        },
-                    )
+                },
+                onDismissAction = {
+                    // TODO: b/341320146 - Log previous tooltip was dismissed
+                },
+            )
 
-                windowingEducationViewController.showEducationTooltip(
-                    taskId = captionState.runningTaskInfo.taskId,
-                    tooltipViewConfig = windowingImageButtonTooltipConfig,
-                )
-            }
+        windowingEducationViewController.showEducationTooltip(
+            taskId = captionState.runningTaskInfo.taskId,
+            tooltipViewConfig = windowingImageButtonTooltipConfig,
+        )
     }
 
     /** Show tooltip that points to app chip button and educates user on how to exit desktop mode */
-    private suspend fun showExitWindowingTooltip(tooltipColorScheme: TooltipColorScheme) {
-        windowDecorCaptionHandleRepository.captionStateFlow
-            // After the previous tooltip was dismissed, wait for 400 ms and see if the user entered
-            // desktop mode.
-            .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds)
-            .catchTimeoutAndLog {
-                // TODO: b/341320146 - Log previous tooltip was dismissed
-            }
-            // Wait for few milliseconds before emitting the latest state.
-            .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
-            .filter { captionState ->
-                // Filter out states when app header is not visible or expanded.
-                captionState is CaptionState.AppHeader && !captionState.isHeaderMenuExpanded
-            }
-            // Before showing this tooltip, stop listening to further emissions to avoid
-            // accidentally
-            // showing the same tooltip on future emissions.
-            .take(1)
-            .flowOn(backgroundDispatcher)
-            .collectLatest { captionState ->
-                captionState as CaptionState.AppHeader
-                val globalAppChipBounds = captionState.globalAppChipBounds
-                val tooltipGlobalCoordinates =
-                    Point(
-                        globalAppChipBounds.right,
-                        globalAppChipBounds.top + globalAppChipBounds.height() / 2,
-                    )
-                // Populate information important to inflate exit desktop mode education tooltip.
-                val exitWindowingTooltipConfig =
-                    TooltipEducationViewConfig(
-                        tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip,
-                        tooltipColorScheme = tooltipColorScheme,
-                        tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
-                        tooltipText =
-                            getString(R.string.windowing_desktop_mode_exit_education_tooltip),
-                        arrowDirection =
-                            DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT,
-                        onDismissAction = {},
-                        onEducationClickAction = {
-                            openHandleMenuCallback(captionState.runningTaskInfo.taskId)
-                        },
-                    )
-                windowingEducationViewController.showEducationTooltip(
-                    taskId = captionState.runningTaskInfo.taskId,
-                    tooltipViewConfig = exitWindowingTooltipConfig,
-                )
-            }
-    }
-
-    private fun tooltipColorScheme(captionState: CaptionState): TooltipColorScheme {
-        val onTertiaryFixed =
-            context.getColor(com.android.internal.R.color.materialColorOnTertiaryFixed)
-        val tertiaryFixed =
-            context.getColor(com.android.internal.R.color.materialColorTertiaryFixed)
-
-        return TooltipColorScheme(tertiaryFixed, onTertiaryFixed, onTertiaryFixed)
+    private suspend fun showExitWindowingTooltip(captionState: CaptionState.AppHeader) {
+        val globalAppChipBounds = captionState.globalAppChipBounds
+        val tooltipGlobalCoordinates =
+            Point(
+                if (isRtl()) {
+                    globalAppChipBounds.left
+                } else {
+                    globalAppChipBounds.right
+                },
+                globalAppChipBounds.top + globalAppChipBounds.height() / 2,
+            )
+        // Populate information important to inflate exit desktop mode education tooltip.
+        val exitWindowingTooltipConfig =
+            TooltipEducationViewConfig(
+                tooltipViewLayout = R.layout.desktop_windowing_education_horizontal_arrow_tooltip,
+                tooltipColorScheme =
+                    TooltipColorScheme(
+                        tertiaryFixedColor,
+                        onTertiaryFixedColor,
+                        onTertiaryFixedColor,
+                    ),
+                tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
+                tooltipText = getString(R.string.windowing_desktop_mode_exit_education_tooltip),
+                arrowDirection =
+                    DesktopWindowingEducationTooltipController.TooltipArrowDirection.HORIZONTAL,
+                onDismissAction = {
+                    // TODO: b/341320146 - Log previous tooltip was dismissed
+                },
+                onEducationClickAction = {
+                    openHandleMenuCallback(captionState.runningTaskInfo.taskId)
+                },
+            )
+        windowingEducationViewController.showEducationTooltip(
+            taskId = captionState.runningTaskInfo.taskId,
+            tooltipViewConfig = exitWindowingTooltipConfig,
+        )
     }
 
     /**
@@ -319,43 +313,20 @@
         this.toDesktopModeCallback = toDesktopModeCallback
     }
 
-    private inline fun <T> Flow<T>.catchTimeoutAndLog(crossinline block: () -> Unit) =
-        catch { exception ->
-            if (exception is TimeoutCancellationException) block() else throw exception
-        }
-
-    private fun launchWithExceptionHandling(block: suspend () -> Unit) =
-        applicationCoroutineScope.launch {
-            try {
-                block()
-            } catch (e: Throwable) {
-                Slog.e(TAG, "Error: ", e)
-            }
-        }
-
-    /**
-     * Listens to the changes to [WindowingEducationProto#hasAppHandleHintViewedTimestampMillis()]
-     * in datastore proto object.
-     *
-     * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this flow will always emit false. That
-     * means it will always emit app handle hint has not been viewed yet.
-     */
-    private fun isAppHandleHintViewedFlow(): Flow<Boolean> =
-        appHandleEducationDatastoreRepository.dataStoreFlow
-            .map { preferences ->
-                preferences.hasAppHandleHintViewedTimestampMillis() &&
-                    !SHOULD_OVERRIDE_EDUCATION_CONDITIONS
-            }
-            .distinctUntilChanged()
-
-    /**
-     * Listens to the changes to [WindowingEducationProto#hasAppHandleHintUsedTimestampMillis()] in
-     * datastore proto object.
-     */
-    private suspend fun isAppHandleHintUsed(): Boolean =
+    private suspend fun isAppHandleHintViewed(): Boolean =
         appHandleEducationDatastoreRepository.dataStoreFlow
             .first()
-            .hasAppHandleHintUsedTimestampMillis()
+            .hasAppHandleHintViewedTimestampMillis() && !FORCE_SHOW_DESKTOP_MODE_EDUCATION
+
+    private suspend fun isEnterDesktopModeHintViewed(): Boolean =
+        appHandleEducationDatastoreRepository.dataStoreFlow
+            .first()
+            .hasEnterDesktopModeHintViewedTimestampMillis() && !FORCE_SHOW_DESKTOP_MODE_EDUCATION
+
+    private suspend fun isExitDesktopModeHintViewed(): Boolean =
+        appHandleEducationDatastoreRepository.dataStoreFlow
+            .first()
+            .hasExitDesktopModeHintViewedTimestampMillis() && !FORCE_SHOW_DESKTOP_MODE_EDUCATION
 
     private fun getSize(@DimenRes resourceId: Int): Int {
         if (resourceId == Resources.ID_NULL) return 0
@@ -364,18 +335,27 @@
 
     private fun getString(@StringRes resId: Int): String = context.resources.getString(resId)
 
+    private fun isRtl() = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
+
     companion object {
         const val TAG = "AppHandleEducationController"
         val APP_HANDLE_EDUCATION_DELAY_MILLIS: Long
             get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L)
 
-        val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long
-            get() = SystemProperties.getLong("persist.windowing_app_handle_education_timeout", 400L)
+        val ENTER_DESKTOP_MODE_EDUCATION_DELAY_MILLIS: Long
+            get() =
+                SystemProperties.getLong(
+                    "persist.windowing_enter_desktop_mode_education_timeout",
+                    400L,
+                )
 
-        val SHOULD_OVERRIDE_EDUCATION_CONDITIONS: Boolean
+        val TOOLTIP_VISIBLE_DURATION_MILLIS: Long
+            get() = SystemProperties.getLong("persist.windowing_tooltip_visible_duration", 12000L)
+
+        val FORCE_SHOW_DESKTOP_MODE_EDUCATION: Boolean
             get() =
                 SystemProperties.getBoolean(
-                    "persist.desktop_windowing_app_handle_education_override_conditions",
+                    "persist.windowing_force_show_desktop_mode_education",
                     false,
                 )
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt
index 9990846..4d219b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt
@@ -17,13 +17,14 @@
 package com.android.wm.shell.desktopmode.education
 
 import android.annotation.IntegerRes
+import android.app.ActivityManager.RunningTaskInfo
 import android.app.usage.UsageStatsManager
 import android.content.Context
 import android.os.SystemClock
 import android.provider.Settings.Secure
 import com.android.wm.shell.R
 import com.android.wm.shell.desktopmode.CaptionState
-import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.SHOULD_OVERRIDE_EDUCATION_CONDITIONS
+import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.FORCE_SHOW_DESKTOP_MODE_EDUCATION
 import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
 import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
 import java.time.Duration
@@ -37,26 +38,28 @@
     private val usageStatsManager =
         context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
 
-    /**
-     * Returns true if conditions to show app handle education are met, returns false otherwise.
-     *
-     * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this method will always return
-     * ![captionState.isHandleMenuExpanded].
-     */
-    suspend fun shouldShowAppHandleEducation(captionState: CaptionState): Boolean {
-        if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false
-        if (SHOULD_OVERRIDE_EDUCATION_CONDITIONS) return true
+    suspend fun shouldShowDesktopModeEducation(captionState: CaptionState.AppHeader): Boolean =
+        shouldShowDesktopModeEducation(captionState.runningTaskInfo)
 
-        val focusAppPackageName =
-            captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false
+    suspend fun shouldShowDesktopModeEducation(captionState: CaptionState.AppHandle): Boolean =
+        shouldShowDesktopModeEducation(captionState.runningTaskInfo)
+
+    /**
+     * Returns true if conditions to show app handle, enter desktop mode and exit desktop mode
+     * education are met based on the app info and usage, returns false otherwise.
+     *
+     * If [FORCE_SHOW_DESKTOP_MODE_EDUCATION] is true, this method will always return true.
+     */
+    private suspend fun shouldShowDesktopModeEducation(taskInfo: RunningTaskInfo): Boolean {
+        if (FORCE_SHOW_DESKTOP_MODE_EDUCATION) return true
+
+        val focusAppPackageName = taskInfo.topActivityInfo?.packageName ?: return false
         val windowingEducationProto =
             appHandleEducationDatastoreRepository.windowingEducationProto()
 
         return isFocusAppInAllowlist(focusAppPackageName) &&
             !isOtherEducationShowing() &&
             hasSufficientTimeSinceSetup() &&
-            !isAppHandleHintViewedBefore(windowingEducationProto) &&
-            !isAppHandleHintUsedBefore(windowingEducationProto) &&
             hasMinAppUsage(windowingEducationProto, focusAppPackageName)
     }
 
@@ -79,14 +82,6 @@
                 R.integer.desktop_windowing_education_required_time_since_setup_seconds
             )
 
-    private fun isAppHandleHintViewedBefore(
-        windowingEducationProto: WindowingEducationProto
-    ): Boolean = windowingEducationProto.hasAppHandleHintViewedTimestampMillis()
-
-    private fun isAppHandleHintUsedBefore(
-        windowingEducationProto: WindowingEducationProto
-    ): Boolean = windowingEducationProto.hasAppHandleHintUsedTimestampMillis()
-
     private suspend fun hasMinAppUsage(
         windowingEducationProto: WindowingEducationProto,
         focusAppPackageName: String,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
index 3e120b0..d061e03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
@@ -91,6 +91,40 @@
     }
 
     /**
+     * Updates [WindowingEducationProto.enterDesktopModeHintViewedTimestampMillis_] field in
+     * datastore with current timestamp if [isViewed] is true, if not then clears the field.
+     */
+    suspend fun updateEnterDesktopModeHintViewedTimestampMillis(isViewed: Boolean) {
+        dataStore.updateData { preferences ->
+            if (isViewed) {
+                preferences
+                    .toBuilder()
+                    .setEnterDesktopModeHintViewedTimestampMillis(System.currentTimeMillis())
+                    .build()
+            } else {
+                preferences.toBuilder().clearEnterDesktopModeHintViewedTimestampMillis().build()
+            }
+        }
+    }
+
+    /**
+     * Updates [WindowingEducationProto.exitDesktopModeHintViewedTimestampMillis_] field in
+     * datastore with current timestamp if [isViewed] is true, if not then clears the field.
+     */
+    suspend fun updateExitDesktopModeHintViewedTimestampMillis(isViewed: Boolean) {
+        dataStore.updateData { preferences ->
+            if (isViewed) {
+                preferences
+                    .toBuilder()
+                    .setExitDesktopModeHintViewedTimestampMillis(System.currentTimeMillis())
+                    .build()
+            } else {
+                preferences.toBuilder().clearExitDesktopModeHintViewedTimestampMillis().build()
+            }
+        }
+    }
+
+    /**
      * Updates [WindowingEducationProto.appHandleHintUsedTimestampMillis_] field in datastore with
      * current timestamp if [isViewed] is true, if not then clears the field.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
new file mode 100644
index 0000000..47088c0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2025 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.desktopmode.multidesks
+
+import android.os.IBinder
+
+/** Represents shell-started transitions involving desks. */
+sealed class DeskTransition {
+    /** The transition token. */
+    abstract val token: IBinder
+
+    /** A transition to remove a desk and its tasks from a display. */
+    data class RemoveDesk(
+        override val token: IBinder,
+        val displayId: Int,
+        val deskId: Int,
+        val tasks: Set<Int>,
+        val onDeskRemovedListener: OnDeskRemovedListener?,
+    ) : DeskTransition()
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
new file mode 100644
index 0000000..3e49b8a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2025 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.desktopmode.multidesks
+
+import android.os.IBinder
+import android.view.WindowManager.TRANSIT_CLOSE
+import android.window.TransitionInfo
+import com.android.window.flags.Flags
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
+
+/**
+ * Observer of desk-related transitions, such as adding, removing or activating a whole desk. It
+ * tracks pending transitions and updates repository state once they finish.
+ */
+class DesksTransitionObserver(private val desktopUserRepositories: DesktopUserRepositories) {
+    private val deskTransitions = mutableMapOf<IBinder, DeskTransition>()
+
+    /** Adds a pending desk transition to be tracked. */
+    fun addPendingTransition(transition: DeskTransition) {
+        if (!Flags.enableMultipleDesktopsBackend()) return
+        deskTransitions[transition.token] = transition
+    }
+
+    /**
+     * Called when any transition is ready, which may include transitions not tracked by this
+     * observer.
+     */
+    fun onTransitionReady(transition: IBinder, info: TransitionInfo) {
+        if (!Flags.enableMultipleDesktopsBackend()) return
+        val deskTransition = deskTransitions.remove(transition) ?: return
+        val desktopRepository = desktopUserRepositories.current
+        when (deskTransition) {
+            is DeskTransition.RemoveDesk -> {
+                check(info.type == TRANSIT_CLOSE) { "Expected close transition for desk removal" }
+                // TODO: b/362720497 - consider verifying the desk was actually removed through the
+                //  DesksOrganizer. The transition info won't have changes if the desk was not
+                //  visible, such as when dismissing from Overview.
+                val deskId = deskTransition.deskId
+                val displayId = deskTransition.displayId
+                desktopRepository.removeDesk(deskTransition.deskId)
+                deskTransition.onDeskRemovedListener?.onDeskRemoved(displayId, deskId)
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index e24b2c5..e8996bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -31,6 +31,7 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
@@ -125,6 +126,7 @@
          * drag.
          */
         default boolean onUnhandledDrag(@NonNull PendingIntent launchIntent,
+                @UserIdInt int userId,
                 @NonNull DragEvent dragEvent,
                 @NonNull Consumer<Boolean> onFinishCallback) {
             return false;
@@ -444,8 +446,10 @@
             return;
         }
 
+        // TODO(b/391624027): Consider piping through launch intent user if needed later
+        final int userId = launchIntent.getCreatorUserHandle().getIdentifier();
         final boolean handled = notifyListeners(
-                l -> l.onUnhandledDrag(launchIntent, dragEvent, onFinishCallback));
+                l -> l.onUnhandledDrag(launchIntent, userId, dragEvent, onFinishCallback));
         if (!handled) {
             // Nobody handled this, we still have to notify WM
             onFinishCallback.accept(false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index b38a853..897e2d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -29,6 +29,7 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
 import com.android.wm.shell.desktopmode.DesktopRepository;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
@@ -52,6 +53,7 @@
     private final ShellTaskOrganizer mShellTaskOrganizer;
     private final Optional<DesktopUserRepositories> mDesktopUserRepositories;
     private final Optional<DesktopTasksController> mDesktopTasksController;
+    private final DesktopModeLoggerTransitionObserver mDesktopModeLoggerTransitionObserver;
     private final WindowDecorViewModel mWindowDecorationViewModel;
     private final LaunchAdjacentController mLaunchAdjacentController;
     private final Optional<TaskChangeListener> mTaskChangeListener;
@@ -64,6 +66,7 @@
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<DesktopUserRepositories> desktopUserRepositories,
             Optional<DesktopTasksController> desktopTasksController,
+            DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver,
             LaunchAdjacentController launchAdjacentController,
             WindowDecorViewModel windowDecorationViewModel,
             Optional<TaskChangeListener> taskChangeListener) {
@@ -72,6 +75,7 @@
         mWindowDecorationViewModel = windowDecorationViewModel;
         mDesktopUserRepositories = desktopUserRepositories;
         mDesktopTasksController = desktopTasksController;
+        mDesktopModeLoggerTransitionObserver = desktopModeLoggerTransitionObserver;
         mLaunchAdjacentController = launchAdjacentController;
         mTaskChangeListener = taskChangeListener;
         if (shellInit != null) {
@@ -130,6 +134,9 @@
                 repository.removeTask(taskInfo.displayId, taskInfo.taskId);
             }
         }
+        // TODO: b/367268649 - This listener shouldn't need to call the transition observer directly
+        // for logging once the logic in the observer is moved.
+        mDesktopModeLoggerTransitionObserver.onTaskVanished(taskInfo);
         mWindowDecorationViewModel.onTaskVanished(taskInfo);
         updateLaunchAdjacentController();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 52b6c62..31715f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -175,7 +175,9 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         ArrayList<Animator> animations = mAnimations.get(mergeTarget);
         if (animations == null) return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index f8e6285..d666126 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -277,7 +277,8 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder nextTransition, @NonNull TransitionInfo nextInfo,
-            @NonNull SurfaceControl.Transaction nextT, @NonNull IBinder currentTransition,
+            @NonNull SurfaceControl.Transaction nextT, @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder currentTransition,
             @NonNull TransitionFinishCallback nextFinishCallback) {
         final StartedTransition playing = mStartedTransitions.get(currentTransition);
         if (playing == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 20b8c5e..d5cb2e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -24,7 +24,6 @@
 import static com.android.wm.shell.pip.PipTransitionController.ANIM_TYPE_ALPHA;
 import static com.android.wm.shell.pip.PipTransitionController.ANIM_TYPE_BOUNDS;
 
-import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
@@ -42,7 +41,6 @@
 import android.window.TaskSnapshot;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.common.pip.PipUtils;
@@ -110,13 +108,6 @@
 
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
 
-    private final ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
-            ThreadLocal.withInitial(() -> {
-                AnimationHandler handler = new AnimationHandler();
-                handler.setProvider(new SfVsyncFrameCallbackProvider());
-                return handler;
-            });
-
     private PipTransitionAnimator mCurrentAnimator;
     @PipTransitionController.AnimationType
     private int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -210,7 +201,6 @@
         animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         animator.setFloatValues(FRACTION_START, FRACTION_END);
-        animator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
         return animator;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 2f3c152..f0e6ae4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -372,7 +372,9 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         end();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index d3ae411..0fa6a11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -653,7 +653,9 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: merge animation", TAG);
         if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
index 44900ce..65099c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
@@ -38,6 +38,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
 import com.android.wm.shell.common.pip.PipMediaController;
 import com.android.wm.shell.common.pip.PipMediaController.ActionListener;
 import com.android.wm.shell.common.pip.PipMenuController;
@@ -121,6 +122,9 @@
     @NonNull
     private final PipTransitionState mPipTransitionState;
 
+    @NonNull
+    private final PipDisplayLayoutState mPipDisplayLayoutState;
+
     private SurfaceControl mLeash;
 
     private ActionListener mMediaActionListener = new ActionListener() {
@@ -134,7 +138,8 @@
     public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
             PipMediaController mediaController, SystemWindows systemWindows,
             PipUiEventLogger pipUiEventLogger, PipTaskListener pipTaskListener,
-            @NonNull PipTransitionState pipTransitionState, ShellExecutor mainExecutor,
+            @NonNull PipTransitionState pipTransitionState,
+            @NonNull PipDisplayLayoutState pipDisplayLayoutState, ShellExecutor mainExecutor,
             Handler mainHandler) {
         mContext = context;
         mPipBoundsState = pipBoundsState;
@@ -142,6 +147,7 @@
         mSystemWindows = systemWindows;
         mPipTaskListener = pipTaskListener;
         mPipTransitionState = pipTransitionState;
+        mPipDisplayLayoutState = pipDisplayLayoutState;
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
         mPipUiEventLogger = pipUiEventLogger;
@@ -218,7 +224,7 @@
 
         mSystemWindows.addView(mPipMenuView,
                 getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
-                0, SHELL_ROOT_LAYER_PIP);
+                mPipDisplayLayoutState.getDisplayId(), SHELL_ROOT_LAYER_PIP);
         setShellRootAccessibilityWindow();
 
         // Make sure the initial actions are set
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index b1984cc..99c9302 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -219,6 +220,7 @@
         mPipDisplayLayoutState.setDisplayLayout(layout);
 
         mDisplayController.addDisplayChangingController(this);
+        mDisplayController.addDisplayWindowListener(this);
         mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
                 new ImeListener(mDisplayController, mPipDisplayLayoutState.getDisplayId()) {
                     @Override
@@ -297,6 +299,22 @@
         setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
     }
 
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        // If PiP was active on an external display that is removed, clean up states and set
+        // {@link PipDisplayLayoutState} to DEFAULT_DISPLAY.
+        if (Flags.enableConnectedDisplaysPip() && mPipTransitionState.isInPip()
+                && displayId == mPipDisplayLayoutState.getDisplayId()
+                && displayId != DEFAULT_DISPLAY) {
+            mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
+            mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
+
+            mPipDisplayLayoutState.setDisplayId(DEFAULT_DISPLAY);
+            mPipDisplayLayoutState.setDisplayLayout(
+                    mDisplayController.getDisplayLayout(DEFAULT_DISPLAY));
+        }
+    }
+
     /**
      * A callback for any observed transition that contains a display change in its
      * {@link android.window.TransitionRequestInfo},
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
index b3070f2..7169759 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
@@ -23,6 +23,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.view.Display;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.View;
@@ -34,7 +35,9 @@
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.DismissViewUtils;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
 import com.android.wm.shell.shared.bubbles.DismissCircleView;
 import com.android.wm.shell.shared.bubbles.DismissView;
@@ -50,6 +53,9 @@
     /* The multiplier to apply scale the target size by when applying the magnetic field radius */
     private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
 
+    /* The window type to apply to the display */
+    private static final int WINDOW_TYPE = WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+
     /**
      * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
      * PIP.
@@ -84,16 +90,22 @@
     private final Context mContext;
     private final PipMotionHelper mMotionHelper;
     private final PipUiEventLogger mPipUiEventLogger;
-    private final WindowManager mWindowManager;
+    private WindowManager mWindowManager;
+    /** The display id for the display that is associated with mWindowManager. */
+    private int mWindowManagerDisplayId = -1;
+    private final PipDisplayLayoutState mPipDisplayLayoutState;
+    private final DisplayController mDisplayController;
     private final ShellExecutor mMainExecutor;
 
     public PipDismissTargetHandler(Context context, PipUiEventLogger pipUiEventLogger,
-            PipMotionHelper motionHelper, ShellExecutor mainExecutor) {
+            PipMotionHelper motionHelper, PipDisplayLayoutState pipDisplayLayoutState,
+            DisplayController displayController, ShellExecutor mainExecutor) {
         mContext = context;
         mPipUiEventLogger = pipUiEventLogger;
         mMotionHelper = motionHelper;
+        mPipDisplayLayoutState = pipDisplayLayoutState;
+        mDisplayController = displayController;
         mMainExecutor = mainExecutor;
-        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
     }
 
     void init() {
@@ -240,6 +252,8 @@
 
     /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
     public void createOrUpdateDismissTarget() {
+        getWindowManager();
+
         if (mTargetViewContainer.getParent() == null) {
             mTargetViewContainer.cancelAnimators();
 
@@ -262,7 +276,7 @@
                 WindowManager.LayoutParams.MATCH_PARENT,
                 height,
                 0, windowSize.y - height,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WINDOW_TYPE,
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
@@ -308,4 +322,16 @@
             mWindowManager.removeViewImmediate(mTargetViewContainer);
         }
     }
+
+    /** Sets mWindowManager to WindowManager associated with the display where PiP is active on. */
+    private void getWindowManager() {
+        final int pipDisplayId = mPipDisplayLayoutState.getDisplayId();
+        if (mWindowManager != null && pipDisplayId == mWindowManagerDisplayId) {
+            return;
+        }
+        mWindowManagerDisplayId = pipDisplayId;
+        Display display = mDisplayController.getDisplay(mWindowManagerDisplayId);
+        Context uiContext = mContext.createWindowContext(display, WINDOW_TYPE, null);
+        mWindowManager = uiContext.getSystemService(WindowManager.class);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
index ffda56d..0a0ecff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
@@ -16,8 +16,6 @@
 
 package com.android.wm.shell.pip2.phone;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Looper;
@@ -30,6 +28,7 @@
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
 import java.io.PrintWriter;
@@ -84,6 +83,7 @@
     private final IWindowManager mWindowManager;
     private final IBinder mToken;
     private final String mName;
+    private final PipDisplayLayoutState mPipDisplayLayoutState;
     private final ShellExecutor mMainExecutor;
 
     private InputEventReceiver mInputEventReceiver;
@@ -94,10 +94,11 @@
      * @param name the name corresponding to the input consumer that is defined in the system.
      */
     public PipInputConsumer(IWindowManager windowManager, String name,
-            ShellExecutor mainExecutor) {
+            PipDisplayLayoutState pipDisplayLayoutState, ShellExecutor mainExecutor) {
         mWindowManager = windowManager;
         mToken = new Binder();
         mName = name;
+        mPipDisplayLayoutState = pipDisplayLayoutState;
         mMainExecutor = mainExecutor;
     }
 
@@ -138,9 +139,9 @@
         }
         final InputChannel inputChannel = new InputChannel();
         try {
-            // TODO(b/113087003): Support Picture-in-picture in multi-display.
-            mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
-            mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
+            final int displayId = mPipDisplayLayoutState.getDisplayId();
+            mWindowManager.destroyInputConsumer(mToken, displayId);
+            mWindowManager.createInputConsumer(mToken, mName, displayId, inputChannel);
         } catch (RemoteException e) {
             ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                     "%s: Failed to create input consumer, %s", TAG, e);
@@ -162,8 +163,7 @@
             return;
         }
         try {
-            // TODO(b/113087003): Support Picture-in-picture in multi-display.
-            mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
+            mWindowManager.destroyInputConsumer(mToken, mPipDisplayLayoutState.getDisplayId());
         } catch (RemoteException e) {
             ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                     "%s: Failed to destroy input consumer, %s", TAG, e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index d98be55..e4be3f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -44,6 +44,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
 import com.android.wm.shell.common.pip.PipPerfHintController;
 import com.android.wm.shell.common.pip.PipPinchResizingAlgorithm;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
@@ -70,9 +71,9 @@
     private final PipScheduler mPipScheduler;
     private final PipTransitionState mPipTransitionState;
     private final PhonePipMenuController mPhonePipMenuController;
+    private final PipDisplayLayoutState mPipDisplayLayoutState;
     private final PipUiEventLogger mPipUiEventLogger;
     private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
-    private final int mDisplayId;
     private final ShellExecutor mMainExecutor;
 
     private final PointF mDownPoint = new PointF();
@@ -120,10 +121,10 @@
             PipTransitionState pipTransitionState,
             PipUiEventLogger pipUiEventLogger,
             PhonePipMenuController menuActivityController,
+            PipDisplayLayoutState pipDisplayLayoutState,
             ShellExecutor mainExecutor,
             @Nullable PipPerfHintController pipPerfHintController) {
         mContext = context;
-        mDisplayId = context.getDisplayId();
         mMainExecutor = mainExecutor;
         mPipPerfHintController = pipPerfHintController;
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
@@ -135,6 +136,7 @@
         mPipTransitionState.addPipTransitionStateChangedListener(this);
 
         mPhonePipMenuController = menuActivityController;
+        mPipDisplayLayoutState = pipDisplayLayoutState;
         mPipUiEventLogger = pipUiEventLogger;
         mPinchResizingAlgorithm = new PipPinchResizingAlgorithm();
     }
@@ -197,7 +199,7 @@
         if (mIsEnabled) {
             // Register input event receiver
             mInputMonitor = mContext.getSystemService(InputManager.class).monitorGestureInput(
-                    "pip-resize", mDisplayId);
+                    "pip-resize", mPipDisplayLayoutState.getDisplayId());
             try {
                 mMainExecutor.executeBlocking(() -> {
                     mInputEventReceiver = new PipResizeInputEventReceiver(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 2f93715..d735375 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -133,7 +133,9 @@
                 taskInfo.topActivity, mPipTransitionState, mPictureInPictureParams, params);
         setPictureInPictureParams(params);
         float newAspectRatio = mPictureInPictureParams.getAspectRatioFloat();
-        if (PipUtils.aspectRatioChanged(newAspectRatio, mPipBoundsState.getAspectRatio())) {
+        if (params.hasSetAspectRatio()
+                && mPipBoundsAlgorithm.isValidPictureInPictureAspectRatio(newAspectRatio)
+                && PipUtils.aspectRatioChanged(newAspectRatio, mPipBoundsState.getAspectRatio())) {
             mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
                 onAspectRatioChanged(newAspectRatio);
             });
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index fc3fbe2..35cd1a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -54,10 +54,12 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
 import com.android.wm.shell.common.pip.PipDoubleTapHelper;
 import com.android.wm.shell.common.pip.PipPerfHintController;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
@@ -91,6 +93,7 @@
     @NonNull private final PipTransitionState mPipTransitionState;
     @NonNull private final PipScheduler mPipScheduler;
     @NonNull private final SizeSpecSource mSizeSpecSource;
+    @NonNull private final PipDisplayLayoutState mPipDisplayLayoutState;
     private final PipUiEventLogger mPipUiEventLogger;
     private final PipDismissTargetHandler mPipDismissTargetHandler;
     private final ShellExecutor mMainExecutor;
@@ -183,6 +186,8 @@
             @NonNull PipTransitionState pipTransitionState,
             @NonNull PipScheduler pipScheduler,
             @NonNull SizeSpecSource sizeSpecSource,
+            @NonNull PipDisplayLayoutState pipDisplayLayoutState,
+            DisplayController displayController,
             PipMotionHelper pipMotionHelper,
             FloatingContentCoordinator floatingContentCoordinator,
             PipUiEventLogger pipUiEventLogger,
@@ -200,6 +205,7 @@
         mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
         mPipScheduler = pipScheduler;
         mSizeSpecSource = sizeSpecSource;
+        mPipDisplayLayoutState = pipDisplayLayoutState;
         mMenuController = menuController;
         mPipUiEventLogger = pipUiEventLogger;
         mFloatingContentCoordinator = floatingContentCoordinator;
@@ -208,7 +214,7 @@
         mMotionHelper = pipMotionHelper;
         mPipScheduler.setUpdateMovementBoundsRunnable(this::updateMovementBounds);
         mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
-                mMotionHelper, mainExecutor);
+                mMotionHelper, mPipDisplayLayoutState, displayController, mainExecutor);
         mTouchState = new PipTouchState(ViewConfiguration.get(context),
                 () -> {
                     mMenuController.showMenuWithPossibleDelay(MENU_STATE_FULL,
@@ -220,8 +226,7 @@
                 mainExecutor);
         mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm,
                 pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, pipUiEventLogger,
-                menuController, mainExecutor,
-                mPipPerfHintController);
+                menuController, mPipDisplayLayoutState, mainExecutor, mPipPerfHintController);
         mPipBoundsState.addOnAspectRatioChangedCallback(aspectRatio -> {
             updateMinMaxSize(aspectRatio);
             onAspectRatioChanged();
@@ -264,7 +269,7 @@
         mPipDismissTargetHandler.init();
 
         mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
-                INPUT_CONSUMER_PIP, mMainExecutor);
+                INPUT_CONSUMER_PIP, mPipDisplayLayoutState, mMainExecutor);
         mPipInputConsumer.setInputListener(this::handleTouchEvent);
         mPipInputConsumer.setRegistrationListener(this::onRegistrationChanged);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 272cb43..78aa686 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -70,6 +70,7 @@
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
 import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
 import com.android.wm.shell.pip2.animation.PipEnterAnimator;
 import com.android.wm.shell.pip2.animation.PipExpandAnimator;
@@ -115,6 +116,7 @@
     private final PipTransitionState mPipTransitionState;
     private final PipDisplayLayoutState mPipDisplayLayoutState;
     private final DisplayController mDisplayController;
+    private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
     private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional;
     private final Optional<DesktopWallpaperActivityTokenProvider>
             mDesktopWallpaperActivityTokenProviderOptional;
@@ -171,6 +173,7 @@
         mPipTransitionState.addPipTransitionStateChangedListener(this);
         mPipDisplayLayoutState = pipDisplayLayoutState;
         mDisplayController = displayController;
+        mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(mContext);
         mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional;
         mDesktopWallpaperActivityTokenProviderOptional =
                 desktopWallpaperActivityTokenProviderOptional;
@@ -244,7 +247,9 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         // Just jump-cut the current animation if any, but do not merge.
         if (info.getType() == TRANSIT_EXIT_PIP) {
@@ -343,6 +348,25 @@
         }
     }
 
+    @Override
+    public boolean syncPipSurfaceState(@NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction) {
+        final TransitionInfo.Change pipChange = getPipChange(info);
+        if (pipChange == null) return false;
+
+        // add shadow and corner radii
+        final SurfaceControl leash = pipChange.getLeash();
+        final boolean isInPip = mPipTransitionState.isInPip();
+
+        mPipSurfaceTransactionHelper.round(startTransaction, leash, isInPip)
+                .shadow(startTransaction, leash, isInPip);
+        mPipSurfaceTransactionHelper.round(finishTransaction, leash, isInPip)
+                .shadow(finishTransaction, leash, isInPip);
+
+        return true;
+    }
+
     //
     // Animation schedulers and entry points
     //
@@ -703,6 +727,12 @@
                 mPipTransitionState.getPipTaskToken());
         mFinishCallback = finishCallback;
 
+        if (isPipClosing(info)) {
+            // If PiP is removed via a close (e.g. finishing of the activity), then
+            // clear out the PiP cache related to that activity component (e.g. reentry state).
+            mPipBoundsState.setLastPipComponentName(null /* lastPipComponentName */);
+        }
+
         finishTransaction.setAlpha(pipChange.getLeash(), 0f);
         if (mPendingRemoveWithFadeout) {
             PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipChange.getLeash(),
@@ -930,13 +960,29 @@
 
         boolean isPipMovedToBack = info.getType() == TRANSIT_TO_BACK
                 && pipChange.getMode() == TRANSIT_TO_BACK;
-        boolean isPipClosed = info.getType() == TRANSIT_CLOSE
-                && pipChange.getMode() == TRANSIT_CLOSE;
         // If PiP is dismissed by user (i.e. via dismiss button in PiP menu)
         boolean isPipDismissed = info.getType() == TRANSIT_REMOVE_PIP
                 && pipChange.getMode() == TRANSIT_TO_BACK;
         // PiP is being removed if the pinned task is either moved to back, closed, or dismissed.
-        return isPipMovedToBack || isPipClosed || isPipDismissed;
+        return isPipMovedToBack || isPipClosing(info) || isPipDismissed;
+    }
+
+    private boolean isPipClosing(@NonNull TransitionInfo info) {
+        if (mPipTransitionState.getPipTaskToken() == null) {
+            // PiP removal makes sense if enter-PiP has cached a valid pinned task token.
+            return false;
+        }
+        TransitionInfo.Change pipChange = info.getChange(mPipTransitionState.getPipTaskToken());
+        TransitionInfo.Change pipActivityChange = info.getChanges().stream().filter(change ->
+                change.getTaskInfo() == null && change.getParent() != null
+                        && change.getParent() == mPipTransitionState.getPipTaskToken())
+                .findFirst().orElse(null);
+
+        boolean isPipTaskClosed = pipChange != null
+                && pipChange.getMode() == TRANSIT_CLOSE;
+        boolean isPipActivityClosed = pipActivityChange != null
+                && pipActivityChange.getMode() == TRANSIT_CLOSE;
+        return isPipTaskClosed || isPipActivityClosed;
     }
 
     private void prepareConfigAtEndActivity(@NonNull SurfaceControl.Transaction startTx,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 2d4d458..4f2e028 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -311,6 +311,9 @@
 
     public void onTaskAdded(RunningTaskInfo taskInfo) {
         notifyRunningTaskAppeared(taskInfo);
+        if (!enableShellTopTaskTracking()) {
+            notifyRecentTasksChanged();
+        }
     }
 
     public void onTaskRemoved(RunningTaskInfo taskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index afc6fee..8ad2e1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -222,7 +222,7 @@
         RecentsMixedHandler mixer = null;
         Consumer<IBinder> setTransitionForMixer = null;
         for (int i = 0; i < mMixers.size(); ++i) {
-            setTransitionForMixer = mMixers.get(i).handleRecentsRequest(wct);
+            setTransitionForMixer = mMixers.get(i).handleRecentsRequest();
             if (setTransitionForMixer != null) {
                 mixer = mMixers.get(i);
                 break;
@@ -307,7 +307,9 @@
 
     @Override
     public void mergeAnimation(IBinder transition, TransitionInfo info,
-            SurfaceControl.Transaction t, IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            IBinder mergeTarget,
             Transitions.TransitionFinishCallback finishCallback) {
         final RecentsController controller = findController(mergeTarget);
         if (controller == null) {
@@ -315,7 +317,7 @@
                     "RecentsTransitionHandler.mergeAnimation: no controller found");
             return;
         }
-        controller.merge(info, t, mergeTarget, finishCallback);
+        controller.merge(info, startT, mergeTarget, finishCallback);
     }
 
     @Override
@@ -1455,6 +1457,11 @@
                 }
             }
 
+            // Notify the mixers of the pending finish
+            for (int i = 0; i < mMixers.size(); ++i) {
+                mMixers.get(i).handleFinishRecents(returningToApp, wct, t);
+            }
+
             if (Flags.enableRecentsBookendTransition()) {
                 if (!wct.isEmpty()) {
                     ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
@@ -1653,15 +1660,22 @@
      */
     public interface RecentsMixedHandler extends Transitions.TransitionHandler {
         /**
-         * Called when a recents request comes in. The handler can add operations to outWCT. If
-         * the handler wants to "accept" the transition, it should return a Consumer accepting the
-         * IBinder for the transition. If not, it should return `null`.
+         * Called when a recents request comes in. If the handler wants to "accept" the transition,
+         * it should return a Consumer accepting the IBinder for the transition. If not, it should
+         * return `null`.
          *
          * If a mixed-handler accepts this recents, it will be the de-facto handler for this
          * transition and is required to call the associated {@link #startAnimation},
          * {@link #mergeAnimation}, and {@link #onTransitionConsumed} methods.
          */
         @Nullable
-        Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT);
+        Consumer<IBinder> handleRecentsRequest();
+
+        /**
+         * Called when a recents transition has finished, with a WCT and SurfaceControl Transaction
+         * that can be used to add to any changes needed to restore the state.
+         */
+        void handleFinishRecents(boolean returnToApp, @NonNull WindowContainerTransaction finishWct,
+                @NonNull SurfaceControl.Transaction finishT);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 99a89a6..ae01592 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -649,11 +649,12 @@
             @Nullable Bundle options, UserHandle user) {
         if (options == null) options = new Bundle();
         final ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+        final int userId = user.getIdentifier();
 
         if (samePackage(packageName, getPackageName(reverseSplitPosition(position), null),
-                user.getIdentifier(), getUserId(reverseSplitPosition(position), null))) {
+                userId, getUserId(reverseSplitPosition(position), null))) {
             if (mMultiInstanceHelpher.supportsMultiInstanceSplit(
-                    getShortcutComponent(packageName, shortcutId, user, mLauncherApps))) {
+                    getShortcutComponent(packageName, shortcutId, user, mLauncherApps), userId)) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else if (isSplitScreenVisible()) {
@@ -687,7 +688,8 @@
         final int userId1 = shortcutInfo.getUserId();
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity(),
+                    userId1)) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
@@ -735,7 +737,8 @@
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         boolean setSecondIntentMultipleTask = false;
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent))) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent),
+                    userId1)) {
                 setSecondIntentMultipleTask = true;
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
@@ -775,7 +778,8 @@
                 ? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
         boolean setSecondIntentMultipleTask = false;
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1),
+                    userId1)) {
                 fillInIntent1 = new Intent();
                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 setSecondIntentMultipleTask = true;
@@ -858,7 +862,7 @@
             return;
         }
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(intent))) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(intent), userId1)) {
                 // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
                 // the split and there is no reusable background task.
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 3091be5..fed336b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -461,12 +461,14 @@
         return transition;
     }
 
-    void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
+    void mergeAnimation(IBinder transition, TransitionInfo info,
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
             IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
         if (mergeTarget != mAnimatingTransition) return;
 
         if (mActiveRemoteHandler != null) {
-            mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+            mActiveRemoteHandler.mergeAnimation(transition, info, startT,
+                    finishT, mergeTarget, finishCallback);
         } else {
             for (int i = mAnimations.size() - 1; i >= 0; --i) {
                 final Animator anim = mAnimations.get(i);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 511e426..6783df8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -109,6 +109,7 @@
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseIntArray;
 import android.view.Choreographer;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
@@ -129,6 +130,7 @@
 import com.android.internal.policy.FoldLockSettingsObserver;
 import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -2975,10 +2977,13 @@
 
     @Override
     public void mergeAnimation(IBinder transition, TransitionInfo info,
-            SurfaceControl.Transaction t, IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            IBinder mergeTarget,
             Transitions.TransitionFinishCallback finishCallback) {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "mergeAnimation: transition=%d", info.getDebugId());
-        mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+        mSplitTransitions.mergeAnimation(transition, info, startT, finishT, mergeTarget,
+                finishCallback);
     }
 
     /** Jump the current transition animation to the end. */
@@ -3011,11 +3016,18 @@
             final int transitType = info.getType();
             TransitionInfo.Change pipChange = null;
             int closingSplitTaskId = -1;
-            // This array tracks where we are sending stages (TO_BACK/TO_FRONT) in this transition.
-            // TODO (b/349828130): Update for n apps (needs to handle different indices than 0/1).
-            //  Also make sure having multiple changes per stage (2+ tasks in one stage) is being
-            //  handled properly.
-            int[] stageChanges = new int[2];
+            // This array tracks if we are sending stages TO_BACK/TO_FRONT in this transition.
+            // TODO (b/349828130): Also make sure having multiple changes per stage (2+ tasks in
+            //  one stage) is being handled properly.
+            SparseIntArray stageChanges = new SparseIntArray();
+            if (enableFlexibleSplit()) {
+                mStageOrderOperator.getActiveStages()
+                        .forEach(stage -> stageChanges.put(stage.getId(), -1));
+            } else {
+                stageChanges.put(STAGE_TYPE_MAIN, -1);
+                stageChanges.put(STAGE_TYPE_SIDE, -1);
+            }
+
 
             for (int iC = 0; iC < info.getChanges().size(); ++iC) {
                 final TransitionInfo.Change change = info.getChanges().get(iC);
@@ -3089,14 +3101,12 @@
                     // we'll break split
                     closingSplitTaskId = taskId;
                 }
-                if (transitType == WindowManager.TRANSIT_WAKE) {
-                    // Record which stages are receiving which changes
-                    if ((change.getMode() == TRANSIT_TO_BACK
-                            || change.getMode() == TRANSIT_TO_FRONT)
-                            && (stageOfTaskId == STAGE_TYPE_MAIN
-                            || stageOfTaskId == STAGE_TYPE_SIDE)) {
-                        stageChanges[stageOfTaskId] = change.getMode();
-                    }
+                // Record which stages are receiving which changes
+                if ((change.getMode() == TRANSIT_TO_BACK
+                        || change.getMode() == TRANSIT_TO_FRONT)
+                        && (stageOfTaskId == STAGE_TYPE_MAIN
+                        || stageOfTaskId == STAGE_TYPE_SIDE)) {
+                    stageChanges.put(getStageOfTask(taskId), change.getMode());
                 }
             }
 
@@ -3125,8 +3135,16 @@
             // If keyguard is active, check to see if we have all our stages showing. If one stage
             // was moved but not the other (which can happen with SHOW_ABOVE_LOCKED apps), we should
             // break split.
-            if (mKeyguardActive && stageChanges[STAGE_TYPE_MAIN] != stageChanges[STAGE_TYPE_SIDE]) {
-                dismissSplitKeepingLastActiveStage(EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
+            if (mKeyguardActive && stageChanges.size() > 0) {
+                int firstChangeMode = stageChanges.valueAt(0);
+                for (int i = 0; i < stageChanges.size(); i++) {
+                    int changeMode = stageChanges.valueAt(i);
+                    // Compare each changeMode to the first one. If any are different, break split.
+                    if (changeMode != firstChangeMode) {
+                        dismissSplitKeepingLastActiveStage(EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
+                        break;
+                    }
+                }
             }
 
             final ArraySet<StageTaskListener> dismissStages = record.getShouldDismissedStage();
@@ -3766,13 +3784,31 @@
         mTaskOrganizer.applyTransaction(wct);
     }
 
+    public void onRecentsInSplitAnimationFinishing(boolean returnToApp,
+            @NonNull WindowContainerTransaction finishWct,
+            @NonNull SurfaceControl.Transaction finishT) {
+        if (!Flags.enableRecentsBookendTransition()) {
+            // The non-bookend recents transition case will be handled by
+            // RecentsMixedTransition wrapping the finish callback and calling
+            // onRecentsInSplitAnimationFinish()
+            return;
+        }
+
+        onRecentsInSplitAnimationFinishInner(returnToApp, finishWct, finishT);
+    }
+
     /** Call this when the recents animation during split-screen finishes. */
-    public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
-            SurfaceControl.Transaction finishT) {
-        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationFinish");
-        mPausingTasks.clear();
+    public void onRecentsInSplitAnimationFinish(@NonNull WindowContainerTransaction finishWct,
+            @NonNull SurfaceControl.Transaction finishT) {
+        if (Flags.enableRecentsBookendTransition()) {
+            // The bookend recents transition case will be handled by
+            // onRecentsInSplitAnimationFinishing above
+            return;
+        }
+
         // Check if the recent transition is finished by returning to the current
         // split, so we can restore the divider bar.
+        boolean returnToApp = false;
         for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
             final WindowContainerTransaction.HierarchyOp op =
                     finishWct.getHierarchyOps().get(i);
@@ -3787,13 +3823,26 @@
             }
             if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
                     && anyStageContainsContainer) {
-                updateSurfaceBounds(mSplitLayout, finishT,
-                        false /* applyResizingOffset */);
-                finishT.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash);
-                setDividerVisibility(true, finishT);
-                return;
+                returnToApp = true;
             }
         }
+        onRecentsInSplitAnimationFinishInner(returnToApp, finishWct, finishT);
+    }
+
+    /** Call this when the recents animation during split-screen finishes. */
+    public void onRecentsInSplitAnimationFinishInner(boolean returnToApp,
+            @NonNull WindowContainerTransaction finishWct,
+            @NonNull SurfaceControl.Transaction finishT) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationFinish: returnToApp=%b",
+                returnToApp);
+        mPausingTasks.clear();
+        if (returnToApp) {
+            updateSurfaceBounds(mSplitLayout, finishT,
+                    false /* applyResizingOffset */);
+            finishT.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash);
+            setDividerVisibility(true, finishT);
+            return;
+        }
 
         setSplitsVisible(false);
         finishWct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index 0445add..13d87ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -92,6 +92,10 @@
         getHolder().addCallback(this);
     }
 
+    public TaskViewTaskController getController() {
+        return mTaskViewTaskController;
+    }
+
     /**
      * Launch a new activity.
      *
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index d19a7ea..a0cc2bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -417,7 +417,8 @@
         }
     }
 
-    void notifyTaskRemovalStarted(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
+    /** Notifies listeners of a task being removed. */
+    public void notifyTaskRemovalStarted(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
         if (mListener == null) return;
         final int taskId = taskInfo.taskId;
         mListenerExecutor.execute(() -> mListener.onTaskRemovalStarted(taskId));
@@ -448,7 +449,7 @@
      * have the pending info, we'll do it when we receive it in
      * {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}.
      */
-    void setTaskNotFound() {
+    public void setTaskNotFound() {
         mTaskNotFound = true;
         if (mPendingInfo != null) {
             cleanUpPendingTask();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 6c90a90..9af2308 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -66,7 +67,7 @@
     static final String TAG = "TaskViewTransitions";
 
     /**
-     * Map of {@link TaskViewTaskController} to {@link TaskViewRequestedState}.
+     * Map of {@link TaskViewTaskController} to {@link TaskViewRepository.TaskViewState}.
      * <p>
      * {@link TaskView} keeps a reference to the {@link TaskViewTaskController} instance and
      * manages its lifecycle.
@@ -95,6 +96,7 @@
         final @WindowManager.TransitionType int mType;
         final WindowContainerTransaction mWct;
         final @NonNull TaskViewTaskController mTaskView;
+        ExternalTransition mExternalTransition;
         IBinder mClaimed;
 
         /**
@@ -182,6 +184,32 @@
     }
 
     /**
+     * Starts or queues an "external" runnable into the pending queue. This means it will run
+     * in order relative to the local transitions.
+     *
+     * The external operation *must* call {@link #onExternalDone} once it has finished.
+     *
+     * In practice, the external is usually another transition on a different handler.
+     */
+    public void enqueueExternal(@NonNull TaskViewTaskController taskView, ExternalTransition ext) {
+        final PendingTransition pending = new PendingTransition(
+                TRANSIT_NONE, null /* wct */, taskView, null /* cookie */);
+        pending.mExternalTransition = ext;
+        mPending.add(pending);
+        startNextTransition();
+    }
+
+    /**
+     * An external transition run in this "queue" is required to call this once it becomes ready.
+     */
+    public void onExternalDone(IBinder key) {
+        final PendingTransition pending = findPending(key);
+        if (pending == null) return;
+        mPending.remove(pending);
+        startNextTransition();
+    }
+
+    /**
      * Looks through the pending transitions for a opening transaction that matches the provided
      * `taskView`.
      *
@@ -191,6 +219,7 @@
     PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) {
         for (int i = mPending.size() - 1; i >= 0; --i) {
             if (mPending.get(i).mTaskView != taskView) continue;
+            if (mPending.get(i).mExternalTransition != null) continue;
             if (TransitionUtil.isOpeningType(mPending.get(i).mType)) {
                 return mPending.get(i);
             }
@@ -207,6 +236,7 @@
     PendingTransition findPending(TaskViewTaskController taskView, int type) {
         for (int i = mPending.size() - 1; i >= 0; --i) {
             if (mPending.get(i).mTaskView != taskView) continue;
+            if (mPending.get(i).mExternalTransition != null) continue;
             if (mPending.get(i).mType == type) {
                 return mPending.get(i);
             }
@@ -518,7 +548,11 @@
             // Wait for this to start animating.
             return;
         }
-        pending.mClaimed = mTransitions.startTransition(pending.mType, pending.mWct, this);
+        if (pending.mExternalTransition != null) {
+            pending.mClaimed = pending.mExternalTransition.start();
+        } else {
+            pending.mClaimed = mTransitions.startTransition(pending.mType, pending.mWct, this);
+        }
     }
 
     @Override
@@ -618,9 +652,16 @@
                     }
                     continue;
                 }
-                startTransaction.reparent(chg.getLeash(), tv.getSurfaceControl());
-                finishTransaction.reparent(chg.getLeash(), tv.getSurfaceControl())
-                        .setPosition(chg.getLeash(), 0, 0);
+                final Rect boundsOnScreen = tv.prepareOpen(chg.getTaskInfo(), chg.getLeash());
+                if (boundsOnScreen != null) {
+                    if (wct == null) wct = new WindowContainerTransaction();
+                    updateBounds(tv, boundsOnScreen, startTransaction, finishTransaction,
+                            chg.getTaskInfo(), chg.getLeash(), wct);
+                } else {
+                    startTransaction.reparent(chg.getLeash(), tv.getSurfaceControl());
+                    finishTransaction.reparent(chg.getLeash(), tv.getSurfaceControl())
+                            .setPosition(chg.getLeash(), 0, 0);
+                }
                 changesHandled++;
             }
         }
@@ -641,7 +682,7 @@
     }
 
     @VisibleForTesting
-    void prepareOpenAnimation(TaskViewTaskController taskView,
+    public void prepareOpenAnimation(TaskViewTaskController taskView,
             final boolean newTask,
             SurfaceControl.Transaction startTransaction,
             SurfaceControl.Transaction finishTransaction,
@@ -649,30 +690,8 @@
             WindowContainerTransaction wct) {
         final Rect boundsOnScreen = taskView.prepareOpen(taskInfo, leash);
         if (boundsOnScreen != null) {
-            final SurfaceControl tvSurface = taskView.getSurfaceControl();
-            // Surface is ready, so just reparent the task to this surface control
-            startTransaction.reparent(leash, tvSurface)
-                    .show(leash);
-            // Also reparent on finishTransaction since the finishTransaction will reparent back
-            // to its "original" parent by default.
-            if (finishTransaction != null) {
-                finishTransaction.reparent(leash, tvSurface)
-                        .setPosition(leash, 0, 0)
-                        // TODO: maybe once b/280900002 is fixed this will be unnecessary
-                        .setWindowCrop(leash, boundsOnScreen.width(), boundsOnScreen.height());
-            }
-            if (useRepo()) {
-                final TaskViewRepository.TaskViewState state = mTaskViewRepo.byTaskView(taskView);
-                if (state != null) {
-                    state.mBounds.set(boundsOnScreen);
-                    state.mVisible = true;
-                }
-            } else {
-                updateBoundsState(taskView, boundsOnScreen);
-                updateVisibilityState(taskView, true /* visible */);
-            }
-            wct.setBounds(taskInfo.token, boundsOnScreen);
-            taskView.applyCaptionInsetsIfNeeded();
+            updateBounds(taskView, boundsOnScreen, startTransaction, finishTransaction, taskInfo,
+                    leash, wct);
         } else {
             // The surface has already been destroyed before the task has appeared,
             // so go ahead and hide the task entirely
@@ -695,4 +714,40 @@
 
         taskView.notifyAppeared(newTask);
     }
+
+    private void updateBounds(TaskViewTaskController taskView, Rect boundsOnScreen,
+            SurfaceControl.Transaction startTransaction,
+            SurfaceControl.Transaction finishTransaction,
+            ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+            WindowContainerTransaction wct) {
+        final SurfaceControl tvSurface = taskView.getSurfaceControl();
+        // Surface is ready, so just reparent the task to this surface control
+        startTransaction.reparent(leash, tvSurface)
+                .show(leash);
+        // Also reparent on finishTransaction since the finishTransaction will reparent back
+        // to its "original" parent by default.
+        if (finishTransaction != null) {
+            finishTransaction.reparent(leash, tvSurface)
+                    .setPosition(leash, 0, 0)
+                    .setWindowCrop(leash, boundsOnScreen.width(), boundsOnScreen.height());
+        }
+        if (useRepo()) {
+            final TaskViewRepository.TaskViewState state = mTaskViewRepo.byTaskView(taskView);
+            if (state != null) {
+                state.mBounds.set(boundsOnScreen);
+                state.mVisible = true;
+            }
+        } else {
+            updateBoundsState(taskView, boundsOnScreen);
+            updateVisibilityState(taskView, true /* visible */);
+        }
+        wct.setBounds(taskInfo.token, boundsOnScreen);
+        taskView.applyCaptionInsetsIfNeeded();
+    }
+
+    /** Interface for running an external transition in this object's pending queue. */
+    public interface ExternalTransition {
+        /** Starts a transition and returns an identifying key for lookup. */
+        IBinder start();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 2177986..743bd05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -176,7 +176,9 @@
 
         abstract void mergeAnimation(
                 @NonNull IBinder transition, @NonNull TransitionInfo info,
-                @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+                @NonNull SurfaceControl.Transaction startT,
+                @NonNull SurfaceControl.Transaction finishT,
+                @NonNull IBinder mergeTarget,
                 @NonNull Transitions.TransitionFinishCallback finishCallback);
 
         abstract void onTransitionConsumed(
@@ -367,7 +369,7 @@
     }
 
     @Override
-    public Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT) {
+    public Consumer<IBinder> handleRecentsRequest() {
         if (mRecentsHandler != null) {
             if (mSplitHandler.isSplitScreenVisible()) {
                 return this::setRecentsTransitionDuringSplit;
@@ -383,6 +385,21 @@
         return null;
     }
 
+    @Override
+    public void handleFinishRecents(boolean returnToApp,
+            @NonNull WindowContainerTransaction finishWct,
+            @NonNull SurfaceControl.Transaction finishT) {
+        if (mRecentsHandler != null) {
+            for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
+                final MixedTransition mixed = mActiveTransitions.get(i);
+                if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
+                    ((RecentsMixedTransition) mixed).onAnimateRecentsDuringSplitFinishing(
+                            returnToApp, finishWct, finishT);
+                }
+            }
+        }
+    }
+
     private void setRecentsTransitionDuringSplit(IBinder transition) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
                 + "Split-Screen is foreground, so treat it as Mixed.");
@@ -676,7 +693,9 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         for (int i = 0; i < mActiveTransitions.size(); ++i) {
             if (mActiveTransitions.get(i).mTransition != mergeTarget) continue;
@@ -686,7 +705,7 @@
                 // Already done, so no need to end it.
                 return;
             }
-            mixed.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+            mixed.mergeAnimation(transition, info, startT, finishT, mergeTarget, finishCallback);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index b0547a2..1853ffa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -384,7 +384,8 @@
     @Override
     void mergeAnimation(
             @NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         switch (mType) {
             case TYPE_DISPLAY_AND_SPLIT_CHANGE:
@@ -394,7 +395,7 @@
             case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
                 mPipHandler.end();
                 mActivityEmbeddingController.mergeAnimation(
-                        transition, info, t, mergeTarget, finishCallback);
+                        transition, info, startT, finishT, mergeTarget, finishCallback);
                 return;
             case TYPE_ENTER_PIP_FROM_SPLIT:
                 if (mAnimType == ANIM_TYPE_GOING_HOME) {
@@ -405,28 +406,28 @@
                     mPipHandler.end();
                     if (mLeftoversHandler != null) {
                         mLeftoversHandler.mergeAnimation(
-                                transition, info, t, mergeTarget, finishCallback);
+                                transition, info, startT, finishT, mergeTarget, finishCallback);
                     }
-                } else {
-                    mPipHandler.end();
                 }
                 return;
             case TYPE_KEYGUARD:
-                mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+                mKeyguardHandler.mergeAnimation(transition, info, startT, finishT, mergeTarget,
+                        finishCallback);
                 return;
             case TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE:
                 mPipHandler.end();
                 if (mLeftoversHandler != null) {
                     mLeftoversHandler.mergeAnimation(
-                            transition, info, t, mergeTarget, finishCallback);
+                            transition, info, startT, finishT, mergeTarget, finishCallback);
                 }
                 return;
             case TYPE_UNFOLD:
-                mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+                mUnfoldHandler.mergeAnimation(transition, info, startT, finishT, mergeTarget,
+                        finishCallback);
                 return;
             case TYPE_OPEN_IN_DESKTOP:
                 mDesktopTasksController.mergeAnimation(
-                        transition, info, t, mergeTarget, finishCallback);
+                        transition, info, startT, finishT, mergeTarget, finishCallback);
                 return;
             default:
                 throw new IllegalStateException("Playing a default mixed transition with unknown or"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 36c3e97..28bba2e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -306,10 +306,10 @@
         }
 
         // Early check if the transition doesn't warrant an animation.
-        if (Transitions.isAllNoAnimation(info) || Transitions.isAllOrderOnly(info)
+        if (TransitionUtil.isAllNoAnimation(info) || TransitionUtil.isAllOrderOnly(info)
                 || (info.getFlags() & WindowManager.TRANSIT_FLAG_INVISIBLE) != 0) {
             startTransaction.apply();
-            finishTransaction.apply();
+            // As a contract, finishTransaction should only be applied in Transitions#onFinish
             finishCallback.onTransitionFinished(null /* wct */);
             return true;
         }
@@ -708,7 +708,9 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         ArrayList<Animator> anims = mAnimations.get(mergeTarget);
         if (anims == null) return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
index 30ffdac5..357861c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
@@ -161,13 +161,10 @@
                 pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
                         finishCB);
             }
-            // make a new finishTransaction because pip's startEnterAnimation "consumes" it so
-            // we need a separate one to send over to launcher.
-            SurfaceControl.Transaction otherFinishT = new SurfaceControl.Transaction();
             // Dispatch the rest of the transition normally. This will most-likely be taken by
             // recents or default handler.
             mixed.mLeftoversHandler = player.dispatchTransition(mixed.mTransition, everythingElse,
-                    otherStartT, otherFinishT, finishCB, mixedHandler);
+                    otherStartT, finishTransaction, finishCB, mixedHandler);
         } else {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  Not leaving split, so just "
                     + "forward animation to Pip-Handler.");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 209fc39..ec73738 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -96,7 +96,9 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Merging registered One-shot remote"
                 + " transition %s for (#%d).", mRemote, info.getDebugId());
@@ -111,7 +113,7 @@
                 // process won't be cleared if the remote applied it. We don't actually know if the
                 // remote applied the transaction, but applying twice will break surfaceflinger
                 // so just assume the worst-case and clear the local transaction.
-                t.clear();
+                startT.clear();
                 mMainExecutor.execute(() -> {
                     finishCallback.onTransitionFinished(wct);
                 });
@@ -121,8 +123,8 @@
             // If the remote is actually in the same process, then make a copy of parameters since
             // remote impls assume that they have to clean-up native references.
             final SurfaceControl.Transaction remoteT =
-                    RemoteTransitionHandler.copyIfLocal(t, mRemote.getRemoteTransition());
-            final TransitionInfo remoteInfo = remoteT == t ? info : info.localRemoteCopy();
+                    RemoteTransitionHandler.copyIfLocal(startT, mRemote.getRemoteTransition());
+            final TransitionInfo remoteInfo = remoteT == startT ? info : info.localRemoteCopy();
             mRemote.getRemoteTransition().mergeAnimation(
                     transition, remoteInfo, remoteT, mergeTarget, cb);
         } catch (RemoteException e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index 8cdbe26..f40dc8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -159,6 +159,8 @@
             // If pair-to-pair switching, the post-recents clean-up isn't needed.
             wct = wct != null ? wct : new WindowContainerTransaction();
             if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) {
+                // TODO(b/346588978): Only called if !enableRecentsBookendTransition(), can remove
+                // once that rolls out
                 mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
             } else {
                 // notify pair-to-pair recents animation finish
@@ -177,24 +179,38 @@
         return handled;
     }
 
+    /**
+     * Called when the recents animation during split is about to finish.
+     */
+    void onAnimateRecentsDuringSplitFinishing(boolean returnToApp,
+            @NonNull WindowContainerTransaction finishWct,
+            @NonNull SurfaceControl.Transaction finishT) {
+        if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) {
+            mSplitHandler.onRecentsInSplitAnimationFinishing(returnToApp, finishWct, finishT);
+        }
+    }
+
     @Override
     void mergeAnimation(
             @NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         switch (mType) {
             case TYPE_RECENTS_DURING_DESKTOP:
-                mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+                mLeftoversHandler.mergeAnimation(transition, info, startT, finishT, mergeTarget,
+                        finishCallback);
                 return;
             case TYPE_RECENTS_DURING_KEYGUARD:
                 if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
-                    handoverTransitionLeashes(mInfo, info, t, mFinishT);
+                    handoverTransitionLeashes(mInfo, info, startT, finishT);
                     if (animateKeyguard(
-                            this, info, t, mFinishT, mFinishCB, mKeyguardHandler, mPipHandler)) {
+                            this, info, startT, finishT, mFinishCB, mKeyguardHandler,
+                            mPipHandler)) {
                         finishCallback.onTransitionFinished(null);
                     }
                 }
-                mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
+                mLeftoversHandler.mergeAnimation(transition, info, startT, finishT, mergeTarget,
                         finishCallback);
                 return;
             case TYPE_RECENTS_DURING_SPLIT:
@@ -203,7 +219,8 @@
                     // another pair.
                     mAnimType = DefaultMixedHandler.MixedTransition.ANIM_TYPE_PAIR_TO_PAIR;
                 }
-                mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+                mLeftoversHandler.mergeAnimation(transition, info, startT, finishT, mergeTarget,
+                        finishCallback);
                 return;
             default:
                 throw new IllegalStateException("Playing a Recents mixed transition with unknown or"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index dec28fe..c4a410b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -211,7 +211,9 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget);
         if (remoteTransition == null) return;
@@ -230,7 +232,7 @@
                 // process won't be cleared if the remote applied it. We don't actually know if the
                 // remote applied the transaction, but applying twice will break surfaceflinger
                 // so just assume the worst-case and clear the local transaction.
-                t.clear();
+                startT.clear();
                 mMainExecutor.execute(() -> {
                     if (!mRequestedRemotes.containsKey(mergeTarget)) {
                         Log.e(TAG, "Merged transition finished after it's mergeTarget (the "
@@ -245,8 +247,8 @@
         try {
             // If the remote is actually in the same process, then make a copy of parameters since
             // remote impls assume that they have to clean-up native references.
-            final SurfaceControl.Transaction remoteT = copyIfLocal(t, remote);
-            final TransitionInfo remoteInfo = remoteT == t ? info : info.localRemoteCopy();
+            final SurfaceControl.Transaction remoteT = copyIfLocal(startT, remote);
+            final TransitionInfo remoteInfo = remoteT == startT ? info : info.localRemoteCopy();
             remote.mergeAnimation(transition, remoteInfo, remoteT, mergeTarget, cb);
         } catch (RemoteException e) {
             Log.e(Transitions.TAG, "Error attempting to merge remote transition.", e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 8c9407b..72cbc47 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -672,46 +672,6 @@
         return -1;
     }
 
-    /**
-     * Look through a transition and see if all non-closing changes are no-animation. If so, no
-     * animation should play.
-     */
-    static boolean isAllNoAnimation(TransitionInfo info) {
-        if (isClosingType(info.getType())) {
-            // no-animation is only relevant for launching (open) activities.
-            return false;
-        }
-        boolean hasNoAnimation = false;
-        final int changeSize = info.getChanges().size();
-        for (int i = changeSize - 1; i >= 0; --i) {
-            final TransitionInfo.Change change = info.getChanges().get(i);
-            if (isClosingType(change.getMode())) {
-                // ignore closing apps since they are a side-effect of the transition and don't
-                // animate.
-                continue;
-            }
-            if (change.hasFlags(FLAG_NO_ANIMATION)) {
-                hasNoAnimation = true;
-            } else if (!TransitionUtil.isOrderOnly(change) && !change.hasFlags(FLAG_IS_OCCLUDED)) {
-                // Ignore the order only or occluded changes since they shouldn't be visible during
-                // animation. For anything else, we need to animate if at-least one relevant
-                // participant *is* animated,
-                return false;
-            }
-        }
-        return hasNoAnimation;
-    }
-
-    /**
-     * Check if all changes in this transition are only ordering changes. If so, we won't animate.
-     */
-    static boolean isAllOrderOnly(TransitionInfo info) {
-        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
-            if (!TransitionUtil.isOrderOnly(info.getChanges().get(i))) return false;
-        }
-        return true;
-    }
-
     private Track getOrCreateTrack(int trackId) {
         while (trackId >= mTracks.size()) {
             mTracks.add(new Track());
@@ -962,7 +922,7 @@
                 + " %s is still animating. Notify the animating transition"
                 + " in case they can be merged", ready, playing);
         mTransitionTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
-        playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT,
+        playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT, ready.mFinishT,
                 playing.mToken, (wct) -> onMerged(playingToken, readyToken));
     }
 
@@ -1396,7 +1356,7 @@
             // fast-forward.
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
                     + " into %s via a SLEEP proxy", nextSync, playing);
-            playing.mHandler.mergeAnimation(nextSync.mToken, dummyInfo, dummyT,
+            playing.mHandler.mergeAnimation(nextSync.mToken, dummyInfo, dummyT, dummyT,
                     playing.mToken, (wct) -> {});
             // it's possible to complete immediately. If that happens, just repeat the signal
             // loop until we either finish everything or start playing an animation that isn't
@@ -1444,7 +1404,9 @@
          * @param finishTransaction the transaction given to the handler to be applied after the
          *                       transition animation. Unlike startTransaction, the handler is NOT
          *                       expected to apply this transaction. The Transition system will
-         *                       apply it when finishCallback is called.
+         *                       apply it when finishCallback is called. If additional transitions
+         *                       are merged, then the finish transactions for those transitions
+         *                       will be applied after this transaction.
          * @param finishCallback Call this when finished. This MUST be called on main thread.
          * @return true if transition was handled, false if not (falls-back to default).
          */
@@ -1454,6 +1416,17 @@
                 @NonNull TransitionFinishCallback finishCallback);
 
         /**
+         * See {@link #mergeAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction, SurfaceControl.Transaction, IBinder, TransitionFinishCallback)}
+         *
+         * This deprecated method header is provided until downstream implementation can migrate to
+         * the call that takes both start & finish transactions.
+         */
+        @Deprecated
+        default void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull IBinder mergeTarget, @NonNull TransitionFinishCallback finishCallback) { }
+
+        /**
          * Attempts to merge a different transition's animation into an animation that this handler
          * is currently playing. If a merge is not possible/supported, this should be a no-op.
          *
@@ -1470,14 +1443,25 @@
          *
          * @param transition This is the transition that wants to be merged.
          * @param info Information about what is changing in the transition.
-         * @param t Contains surface changes that resulted from the transition.
+         * @param startTransaction The start transaction containing surface changes that resulted
+         *                         from the incoming transition. This should be applied by this
+         *                         active handler only if it chooses to merge the transition.
+         * @param finishTransaction The finish transaction for the incoming transition. Unlike
+         *                          startTransaction, the handler is NOT expected to apply this
+         *                          transaction. If the transition is merged, the Transition system
+         *                          will apply after finishCallback is called following the finish
+         *                          transaction provided in `#startAnimation()`.
          * @param mergeTarget This is the transition that we are attempting to merge with (ie. the
          *                    one this handler is currently already animating).
          * @param finishCallback Call this if merged. This MUST be called on main thread.
          */
         default void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-                @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
-                @NonNull TransitionFinishCallback finishCallback) { }
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull IBinder mergeTarget, @NonNull TransitionFinishCallback finishCallback) {
+            // Call the legacy implementation by default
+            mergeAnimation(transition, info, startTransaction, mergeTarget, finishCallback);
+        }
 
         /**
          * Checks whether this handler is capable of taking over a transition matching `info`.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index 3e0e15a..7fd19a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -225,7 +225,9 @@
 
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull IBinder mergeTarget,
             @NonNull TransitionFinishCallback finishCallback) {
         if (info.getType() != TRANSIT_CHANGE) {
             return;
@@ -246,7 +248,7 @@
             }
         }
         // Apply changes happening during the unfold animation immediately
-        t.apply();
+        startT.apply();
         finishCallback.onTransitionFinished(null);
 
         if (getDefaultDisplayChange(info) == DefaultDisplayChange.DEFAULT_DISPLAY_FOLD) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
index 7948ead..2b2cdf8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
@@ -16,13 +16,17 @@
 package com.android.wm.shell.windowdecor;
 
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
 import android.content.Context;
 import android.hardware.input.InputManager;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.InputDevice;
+import android.view.InsetsState;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.SurfaceControl;
@@ -33,6 +37,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
@@ -49,7 +54,8 @@
  * Works with decorations that extend {@link CarWindowDecoration}.
  */
 public abstract class CarWindowDecorViewModel
-        implements WindowDecorViewModel, FocusTransitionListener {
+        implements WindowDecorViewModel, FocusTransitionListener,
+        DisplayInsetsController.OnInsetsChangedListener {
     private static final String TAG = "CarWindowDecorViewModel";
 
     private final ShellTaskOrganizer mTaskOrganizer;
@@ -57,31 +63,37 @@
     private final @ShellBackgroundThread ShellExecutor mBgExecutor;
     private final ShellExecutor mMainExecutor;
     private final DisplayController mDisplayController;
+    private final DisplayInsetsController mDisplayInsetsController;
     private final FocusTransitionObserver mFocusTransitionObserver;
     private final SyncTransactionQueue mSyncQueue;
     private final SparseArray<CarWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
     private final WindowDecorViewHostSupplier<WindowDecorViewHost> mWindowDecorViewHostSupplier;
+    private final IActivityTaskManager mActivityTaskManager;
 
     public CarWindowDecorViewModel(
             Context context,
+            @ShellMainThread ShellExecutor mainExecutor,
             @ShellBackgroundThread ShellExecutor bgExecutor,
-            @ShellMainThread ShellExecutor shellExecutor,
             ShellInit shellInit,
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
+            DisplayInsetsController displayInsetsController,
             SyncTransactionQueue syncQueue,
             FocusTransitionObserver focusTransitionObserver,
             WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) {
         mContext = context;
-        mMainExecutor = shellExecutor;
+        mMainExecutor = mainExecutor;
         mBgExecutor = bgExecutor;
         mTaskOrganizer = taskOrganizer;
         mDisplayController = displayController;
+        mDisplayInsetsController = displayInsetsController;
         mFocusTransitionObserver = focusTransitionObserver;
         mSyncQueue = syncQueue;
         mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
+        mActivityTaskManager = ActivityTaskManager.getService();
 
         shellInit.addInitCallback(this::onInit, this);
+        displayInsetsController.addGlobalInsetsChangedListener(this);
     }
 
     private void onInit() {
@@ -187,6 +199,26 @@
         decoration.close();
     }
 
+    @Override
+    public void insetsChanged(int displayId, InsetsState insetsState) {
+        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        try {
+            mActivityTaskManager.getTasks(/* maxNum= */ Integer.MAX_VALUE,
+                            /* filterOnlyVisibleRecents= */ false, /* keepIntentExtra= */ false,
+                            displayId)
+                    .stream().filter(taskInfo -> taskInfo.isVisible && taskInfo.isRunning)
+                    .forEach(taskInfo -> {
+                        final CarWindowDecoration decoration = mWindowDecorByTaskId.get(
+                                taskInfo.taskId);
+                        if (decoration != null) {
+                            decoration.relayout(taskInfo, t, t);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Cannot update decoration on inset change on displayId: " + displayId);
+        }
+    }
+
     /**
      * @return {@code true} if the task/activity associated with {@code taskInfo} should show
      * window decoration.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
index 3943784..3182745 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
@@ -30,7 +30,6 @@
 import android.window.WindowContainerTransaction;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -47,6 +46,7 @@
     private WindowDecorLinearLayout mRootView;
     private @ShellBackgroundThread final ShellExecutor mBgExecutor;
     private final View.OnClickListener mClickListener;
+    private final RelayoutParams mRelayoutParams = new RelayoutParams();
     private final RelayoutResult<WindowDecorLinearLayout> mResult = new RelayoutResult<>();
 
     CarWindowDecoration(
@@ -75,7 +75,8 @@
     @SuppressLint("MissingPermission")
     void relayout(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
-        relayout(taskInfo, startT, finishT, /* isCaptionVisible= */ true);
+        relayout(taskInfo, startT, finishT,
+                /* isCaptionVisible= */ mRelayoutParams.mIsCaptionVisible);
     }
 
     @SuppressLint("MissingPermission")
@@ -84,12 +85,9 @@
             boolean isCaptionVisible) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
-        RelayoutParams relayoutParams = new RelayoutParams();
+        updateRelayoutParams(mRelayoutParams, taskInfo, isCaptionVisible);
 
-        updateRelayoutParams(relayoutParams, taskInfo,
-                mDisplayController.getInsetsState(taskInfo.displayId), isCaptionVisible);
-
-        relayout(relayoutParams, startT, finishT, wct, mRootView, mResult);
+        relayout(mRelayoutParams, startT, finishT, wct, mRootView, mResult);
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
         mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
 
@@ -118,7 +116,6 @@
     private void updateRelayoutParams(
             RelayoutParams relayoutParams,
             ActivityManager.RunningTaskInfo taskInfo,
-            @Nullable InsetsState displayInsetsState,
             boolean isCaptionVisible) {
         relayoutParams.reset();
         relayoutParams.mRunningTaskInfo = taskInfo;
@@ -127,16 +124,19 @@
         relayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
         relayoutParams.mIsCaptionVisible =
                 isCaptionVisible && mIsStatusBarVisible && !mIsKeyguardVisibleAndOccluded;
-        if (displayInsetsState != null) {
-            relayoutParams.mCaptionTopPadding = getTopPadding(
-                    taskInfo.getConfiguration().windowConfiguration.getBounds(),
-                    displayInsetsState);
-        }
+        relayoutParams.mCaptionTopPadding = getTopPadding(taskInfo, relayoutParams);
+
         relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
         relayoutParams.mApplyStartTransactionOnDraw = true;
     }
 
-    private static int getTopPadding(Rect taskBounds, @NonNull InsetsState insetsState) {
+    private int getTopPadding(ActivityManager.RunningTaskInfo taskInfo,
+            RelayoutParams relayoutParams) {
+        Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
+        InsetsState insetsState = mDisplayController.getInsetsState(taskInfo.displayId);
+        if (insetsState == null) {
+            return relayoutParams.mCaptionTopPadding;
+        }
         Insets systemDecor = insetsState.calculateInsets(taskBounds,
                 WindowInsets.Type.systemBars() & ~WindowInsets.Type.captionBar(),
                 false /* ignoreVisibility */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index fad1c9f..6a2a7b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -32,7 +32,6 @@
 
 import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
 import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
-import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing;
 import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod;
 import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason;
 import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
@@ -51,7 +50,6 @@
 import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Point;
@@ -133,6 +131,7 @@
 import com.android.wm.shell.shared.FocusTransitionListener;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
@@ -254,6 +253,7 @@
     private final DesktopModeUiEventLogger mDesktopModeUiEventLogger;
     private final WindowDecorTaskResourceLoader mTaskResourceLoader;
     private final RecentsTransitionHandler mRecentsTransitionHandler;
+    private final DesktopModeCompatPolicy mDesktopModeCompatPolicy;
 
     public DesktopModeWindowDecorViewModel(
             Context context,
@@ -290,7 +290,8 @@
             DesktopModeEventLogger desktopModeEventLogger,
             DesktopModeUiEventLogger desktopModeUiEventLogger,
             WindowDecorTaskResourceLoader taskResourceLoader,
-            RecentsTransitionHandler recentsTransitionHandler) {
+            RecentsTransitionHandler recentsTransitionHandler,
+            DesktopModeCompatPolicy desktopModeCompatPolicy) {
         this(
                 context,
                 shellExecutor,
@@ -332,7 +333,8 @@
                 desktopModeEventLogger,
                 desktopModeUiEventLogger,
                 taskResourceLoader,
-                recentsTransitionHandler);
+                recentsTransitionHandler,
+                desktopModeCompatPolicy);
     }
 
     @VisibleForTesting
@@ -377,7 +379,8 @@
             DesktopModeEventLogger desktopModeEventLogger,
             DesktopModeUiEventLogger desktopModeUiEventLogger,
             WindowDecorTaskResourceLoader taskResourceLoader,
-            RecentsTransitionHandler recentsTransitionHandler) {
+            RecentsTransitionHandler recentsTransitionHandler,
+            DesktopModeCompatPolicy desktopModeCompatPolicy) {
         mContext = context;
         mMainExecutor = shellExecutor;
         mMainHandler = mainHandler;
@@ -447,6 +450,7 @@
         mDesktopModeUiEventLogger = desktopModeUiEventLogger;
         mTaskResourceLoader = taskResourceLoader;
         mRecentsTransitionHandler = recentsTransitionHandler;
+        mDesktopModeCompatPolicy = desktopModeCompatPolicy;
 
         shellInit.addInitCallback(this::onInit, this);
     }
@@ -461,7 +465,7 @@
                 new DesktopModeOnTaskResizeAnimationListener());
         mDesktopTasksController.setOnTaskRepositionAnimationListener(
                 new DesktopModeOnTaskRepositionAnimationListener());
-        if (Flags.enableDesktopRecentsTransitionsCornersBugfix()) {
+        if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()) {
             mRecentsTransitionHandler.addTransitionStateListener(
                     new DesktopModeRecentsTransitionStateListener());
         }
@@ -1220,12 +1224,6 @@
                         mDragPointerId = e.getPointerId(0);
                     }
                     final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
-                    // Position of the task is calculated by subtracting the raw location of the
-                    // motion event (the location of the motion relative to the display) by the
-                    // location of the motion event relative to the task's bounds
-                    final Point position = new Point(
-                            (int) (e.getRawX(dragPointerIdx) - e.getX(dragPointerIdx)),
-                            (int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx)));
                     final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd(
                             e.getDisplayId(),
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
@@ -1233,7 +1231,7 @@
                     // DesktopTasksController to allow secondary transformations (i.e. snap resizing
                     // or transforming to fullscreen) before setting new task bounds.
                     mDesktopTasksController.onDragPositioningEnd(
-                            taskInfo, decoration.mTaskSurface, position,
+                            taskInfo, decoration.mTaskSurface,
                             new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
                             newTaskBounds, decoration.calculateValidDragArea(),
                             new Rect(mOnDragStartInitialBounds), e,
@@ -1652,11 +1650,7 @@
                 && mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) {
             return false;
         }
-        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
-                && isTopActivityExemptFromDesktopWindowing(mContext, taskInfo)) {
-            return false;
-        }
-        if (isPartOfDefaultHomePackage(taskInfo)) {
+        if (mDesktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) {
             return false;
         }
         final boolean isOnLargeScreen = taskInfo.getConfiguration().smallestScreenWidthDp
@@ -1674,14 +1668,6 @@
                 && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop();
     }
 
-    private boolean isPartOfDefaultHomePackage(RunningTaskInfo taskInfo) {
-        final ComponentName currentDefaultHome =
-                mContext.getPackageManager().getHomeActivities(new ArrayList<>());
-        return currentDefaultHome != null && taskInfo.baseActivity != null
-                && currentDefaultHome.getPackageName()
-                .equals(taskInfo.baseActivity.getPackageName());
-    }
-
     private void createWindowDecoration(
             ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index b179741..c1a6240 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -27,18 +27,17 @@
 import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
 import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
 
-
 import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode;
 import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopModeOrShowAppHandle;
 import static com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.windowdecor.DragPositioningCallbackUtility.DragEventListener;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.DisabledEdge;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.DisabledEdge.NONE;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeHandleEdgeInset;
-import static com.android.wm.shell.windowdecor.DragPositioningCallbackUtility.DragEventListener;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -112,14 +111,14 @@
 import kotlin.jvm.functions.Function0;
 import kotlin.jvm.functions.Function1;
 
-import kotlinx.coroutines.CoroutineScope;
-import kotlinx.coroutines.MainCoroutineDispatcher;
-
 import java.util.List;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
+import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.MainCoroutineDispatcher;
+
 /**
  * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
  * {@link DesktopModeWindowDecorViewModel}.
@@ -542,6 +541,9 @@
                 if (appHeader != null) {
                     appHeader.setAppName(name);
                     appHeader.setAppIcon(icon);
+                    if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
+                        notifyCaptionStateChanged();
+                    }
                 }
             });
         }
@@ -576,6 +578,7 @@
             closeHandleMenu();
             closeManageWindowsMenu();
             closeMaximizeMenu();
+            notifyNoCaptionHandle();
         }
         updateDragResizeListener(oldDecorationSurface, inFullImmersive);
         updateMaximizeMenu(startT, inFullImmersive);
@@ -714,7 +717,6 @@
     }
 
     private void notifyCaptionStateChanged() {
-        // TODO: b/366159408 - Ensure bounds sent with notification account for RTL mode.
         if (!canEnterDesktopMode(mContext) || !isEducationEnabled()) {
             return;
         }
@@ -731,7 +733,7 @@
             // [AppHeaderViewHolder].
             ((AppHeaderViewHolder) mWindowDecorViewHolder).runOnAppChipGlobalLayout(
                     () -> {
-                        notifyAppChipStateChanged();
+                        notifyAppHeaderStateChanged();
                         return Unit.INSTANCE;
                     });
         }
@@ -763,7 +765,10 @@
                 mResult.mCaptionHeight);
     }
 
-    private void notifyAppChipStateChanged() {
+    private void notifyAppHeaderStateChanged() {
+        if (isAppHandle(mWindowDecorViewHolder) || mWindowDecorViewHolder == null) {
+            return;
+        }
         final Rect appChipPositionInWindow =
                 ((AppHeaderViewHolder) mWindowDecorViewHolder).getAppChipLocationInWindow();
         final Rect taskBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
@@ -841,6 +846,9 @@
                     mOnCaptionButtonClickListener,
                     mOnCaptionLongClickListener,
                     mOnCaptionGenericMotionListener,
+                    mOnLeftSnapClickListener,
+                    mOnRightSnapClickListener,
+                    mOnMaximizeOrRestoreClickListener,
                     mOnMaximizeHoverListener);
         }
         throw new IllegalArgumentException("Unexpected layout resource id");
@@ -969,7 +977,7 @@
             final RelayoutParams.OccludingCaptionElement controlsElement =
                     new RelayoutParams.OccludingCaptionElement();
             controlsElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_end;
-            if (Flags.enableMinimizeButton()) {
+            if (DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()) {
                 controlsElement.mWidthResId =
                       R.dimen.desktop_mode_customizable_caption_with_minimize_button_margin_end;
             }
@@ -1355,8 +1363,8 @@
         mWebUri = assistContent == null ? null : AppToWebUtils.getSessionWebUri(assistContent);
         updateGenericLink();
         final boolean supportsMultiInstance = mMultiInstanceHelper
-                .supportsMultiInstanceSplit(mTaskInfo.baseActivity)
-                && Flags.enableDesktopWindowingMultiInstanceFeatures();
+                .supportsMultiInstanceSplit(mTaskInfo.baseActivity, mTaskInfo.userId)
+                && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES.isTrue();
         final boolean shouldShowManageWindowsButton = supportsMultiInstance
                 && mMinimumInstancesFound;
         final boolean shouldShowChangeAspectRatioButton = HandleMenu.Companion
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index e5c989e..32a2f82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -44,18 +44,21 @@
 import androidx.annotation.StringRes
 import androidx.annotation.VisibleForTesting
 import androidx.compose.ui.graphics.toArgb
+import androidx.core.view.ViewCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK
 import androidx.core.view.isGone
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread
 import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
 import com.android.wm.shell.shared.split.SplitScreenConstants
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
-import com.android.wm.shell.windowdecor.common.calculateMenuPosition
 import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
+import com.android.wm.shell.windowdecor.common.calculateMenuPosition
 import com.android.wm.shell.windowdecor.extension.isFullscreen
 import com.android.wm.shell.windowdecor.extension.isMultiWindow
 import com.android.wm.shell.windowdecor.extension.isPinned
@@ -535,6 +538,20 @@
                 }
                 return@setOnTouchListener true
             }
+
+            with(context.resources) {
+                // Update a11y read out to say "double tap to enter desktop windowing mode"
+                ViewCompat.replaceAccessibilityAction(
+                    desktopBtn, ACTION_CLICK,
+                    getString(R.string.app_handle_menu_talkback_desktop_mode_button_text), null
+                )
+
+                // Update a11y read out to say "double tap to enter split screen mode"
+                ViewCompat.replaceAccessibilityAction(
+                    splitscreenBtn, ACTION_CLICK,
+                    getString(R.string.app_handle_menu_talkback_split_screen_mode_button_text), null
+                )
+            }
         }
 
         /** Binds the menu views to the new data. */
@@ -645,7 +662,7 @@
         private fun bindWindowingPill(style: MenuStyle) {
             windowingPill.background.setTint(style.backgroundColor)
 
-            if (!com.android.wm.shell.Flags.enableBubbleAnything()) {
+            if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
                 floatingBtn.visibility = View.GONE
             }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 1ce0366..be3ea4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -34,6 +34,7 @@
 import android.graphics.drawable.ShapeDrawable
 import android.graphics.drawable.StateListDrawable
 import android.graphics.drawable.shapes.RoundRectShape
+import android.os.Bundle
 import android.util.StateSet
 import android.view.LayoutInflater
 import android.view.MotionEvent.ACTION_HOVER_ENTER
@@ -51,12 +52,16 @@
 import android.view.WindowManager
 import android.view.WindowlessWindowManager
 import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
 import android.widget.Button
 import android.widget.TextView
 import android.window.TaskConstants
 import androidx.compose.material3.ColorScheme
 import androidx.compose.ui.graphics.toArgb
 import androidx.core.animation.addListener
+import androidx.core.view.ViewCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
 import androidx.core.view.isGone
 import androidx.core.view.isVisible
 import com.android.wm.shell.R
@@ -403,6 +408,96 @@
                 true
             }
 
+            sizeToggleButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
+                override fun onInitializeAccessibilityNodeInfo(
+                    host: View,
+                    info: AccessibilityNodeInfo
+                ) {
+
+                    super.onInitializeAccessibilityNodeInfo(host, info)
+                    info.addAction(AccessibilityAction.ACTION_CLICK)
+                    host.isClickable = true
+                }
+
+                override fun performAccessibilityAction(
+                    host: View,
+                    action: Int,
+                    args: Bundle?
+                ): Boolean {
+                    if (action == AccessibilityAction.ACTION_CLICK.id) {
+                        onMaximizeClickListener?.invoke()
+                    }
+                    return super.performAccessibilityAction(host, action, args)
+                }
+            }
+
+            snapLeftButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
+                override fun onInitializeAccessibilityNodeInfo(
+                    host: View,
+                    info: AccessibilityNodeInfo
+                ) {
+                    super.onInitializeAccessibilityNodeInfo(host, info)
+                    info.addAction(AccessibilityAction.ACTION_CLICK)
+                    host.isClickable = true
+                }
+
+                override fun performAccessibilityAction(
+                    host: View,
+                    action: Int,
+                    args: Bundle?
+                ): Boolean {
+                    if (action == AccessibilityAction.ACTION_CLICK.id) {
+                        onLeftSnapClickListener?.invoke()
+                    }
+                    return super.performAccessibilityAction(host, action, args)
+                }
+            }
+
+            snapRightButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
+                override fun onInitializeAccessibilityNodeInfo(
+                    host: View,
+                    info: AccessibilityNodeInfo
+                ) {
+                    super.onInitializeAccessibilityNodeInfo(host, info)
+                    info.addAction(AccessibilityAction.ACTION_CLICK)
+                    host.isClickable = true
+                }
+
+                override fun performAccessibilityAction(
+                    host: View,
+                    action: Int,
+                    args: Bundle?
+                ): Boolean {
+                    if (action == AccessibilityAction.ACTION_CLICK.id) {
+                        onRightSnapClickListener?.invoke()
+                    }
+                    return super.performAccessibilityAction(host, action, args)
+                }
+            }
+
+            with(context.resources) {
+                ViewCompat.replaceAccessibilityAction(
+                    snapLeftButton,
+                    AccessibilityActionCompat.ACTION_CLICK,
+                    getString(R.string.maximize_menu_talkback_action_snap_left_text),
+                    null
+                )
+
+                ViewCompat.replaceAccessibilityAction(
+                    snapRightButton,
+                    AccessibilityActionCompat.ACTION_CLICK,
+                    getString(R.string.maximize_menu_talkback_action_snap_right_text),
+                    null
+                )
+
+                ViewCompat.replaceAccessibilityAction(
+                    sizeToggleButton,
+                    AccessibilityActionCompat.ACTION_CLICK,
+                    getString(R.string.maximize_menu_talkback_action_maximize_restore_text),
+                    null
+                )
+            }
+
             // Maximize/restore button.
             val sizeToggleBtnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
                 R.string.desktop_mode_maximize_menu_restore_button_text
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
index 70c0b54..22bc978 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
@@ -5,6 +5,7 @@
 import android.content.Context
 import android.graphics.PointF
 import android.graphics.Rect
+import android.view.Choreographer
 import android.view.MotionEvent
 import android.view.SurfaceControl
 import android.view.VelocityTracker
@@ -48,7 +49,7 @@
                     t.setScale(taskSurface, scale, scale)
                         .setCornerRadius(taskSurface, cornerRadius)
                         .setScale(taskSurface, scale, scale)
-                        .setCornerRadius(taskSurface, cornerRadius)
+                        .setFrameTimeline(Choreographer.getInstance().vsyncId)
                         .setPosition(taskSurface, position.x, position.y)
                         .apply()
                 }
@@ -96,6 +97,7 @@
         setTaskPosition(ev.rawX, ev.rawY)
         val t = transactionFactory()
         t.setPosition(taskSurface, position.x, position.y)
+        t.setFrameTimeline(Choreographer.getInstance().vsyncId)
         t.apply()
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt
index 1bc48f8..801048a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt
@@ -153,9 +153,7 @@
     private fun loadAppResources(taskInfo: RunningTaskInfo): AppResources {
         Trace.beginSection("$TAG#loadAppResources")
         try {
-            val pm = checkNotNull(userProfilesContexts[taskInfo.userId]?.packageManager) {
-                "Could not get context for user ${taskInfo.userId}"
-            }
+            val pm = userProfilesContexts.getOrCreate(taskInfo.userId).packageManager
             val activityInfo = getActivityInfo(taskInfo, pm)
             val appName = pm.getApplicationLabel(activityInfo.applicationInfo)
             val appIconDrawable = iconProvider.getIcon(activityInfo)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt
index 4fa2744..7ffa74f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt
@@ -26,6 +26,7 @@
 import android.view.LayoutInflater
 import android.view.MotionEvent
 import android.view.View
+import android.view.View.LAYOUT_DIRECTION_RTL
 import android.view.View.MeasureSpec.UNSPECIFIED
 import android.view.WindowManager
 import android.widget.ImageView
@@ -52,7 +53,6 @@
     private val additionalSystemViewContainerFactory: AdditionalSystemViewContainer.Factory,
     private val displayController: DisplayController,
 ) : OnDisplayChangingListener {
-  // TODO: b/369384567 - Set tooltip color scheme to match LT/DT of app theme
   private var tooltipView: View? = null
   private var animator: PhysicsAnimator<View>? = null
   private val springConfig by lazy {
@@ -89,7 +89,7 @@
   }
 
   /** Hide the current education view if visible */
-  private fun hideEducationTooltip() = animateHideTooltipTransition { cleanUp() }
+  fun hideEducationTooltip() = animateHideTooltipTransition { cleanUp() }
 
   /** Create education view by inflating layout provided. */
   private fun createEducationTooltipView(
@@ -197,8 +197,9 @@
       background.setTint(tooltipColorScheme.container)
     }
     requireViewById<ImageView>(R.id.arrow_icon).apply {
-      val wrappedDrawable = DrawableCompat.wrap(this.drawable)
-      DrawableCompat.setTint(wrappedDrawable, tooltipColorScheme.container)
+        val wrappedDrawable = DrawableCompat.wrap(this.drawable)
+        DrawableCompat.setTint(wrappedDrawable, tooltipColorScheme.container)
+        if (isRtl()) scaleX = -1f
     }
     requireViewById<TextView>(R.id.tooltip_text).apply { setTextColor(tooltipColorScheme.text) }
     requireViewById<ImageView>(R.id.tooltip_icon).apply {
@@ -227,6 +228,9 @@
       // Arrow is placed at vertical center on the left edge of the tooltip. Hence decrement
       // half of tooltip height from [tooltipY] to vertically position the tooltip.
       tooltipY -= tooltipDimen.height / 2
+      if (isRtl()) {
+          tooltipX -= tooltipDimen.width
+      }
     }
     return Point(tooltipX, tooltipY)
   }
@@ -260,7 +264,9 @@
     return context.resources.getDimensionPixelSize(resourceId)
   }
 
-  /**
+    private fun isRtl() = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
+
+    /**
    * The configuration for education view features:
    *
    * @property tooltipViewLayout Layout resource ID of the view to be used for education tooltip.
@@ -297,6 +303,6 @@
   /** Direction of arrow of the tooltip */
   enum class TooltipArrowDirection {
     UP,
-    LEFT,
+    HORIZONTAL
   }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
index d72da3a..8747f63 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
@@ -29,6 +29,7 @@
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.common.DisplayChangeController
 import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger
 import com.android.wm.shell.desktopmode.DesktopTasksController
@@ -37,6 +38,7 @@
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread
 import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.transition.FocusTransitionObserver
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
 import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
@@ -58,6 +60,8 @@
     private val desktopUserRepositories: DesktopUserRepositories,
     private val desktopModeEventLogger: DesktopModeEventLogger,
     private val taskResourceLoader: WindowDecorTaskResourceLoader,
+    private val focusTransitionObserver: FocusTransitionObserver,
+    private val mainExecutor: ShellExecutor,
 ) : DisplayChangeController.OnDisplayChangingListener {
     @VisibleForTesting
     var tilingTransitionHandlerByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
@@ -94,6 +98,8 @@
                             returnToDragStartAnimator,
                             desktopUserRepositories,
                             desktopModeEventLogger,
+                            focusTransitionObserver,
+                            mainExecutor,
                         )
                     tilingTransitionHandlerByDisplayId.put(displayId, newHandler)
                     newHandler
@@ -112,9 +118,10 @@
     }
 
     fun moveTaskToFrontIfTiled(taskInfo: RunningTaskInfo): Boolean {
+        // Always pass focus=true because taskInfo.isFocused is not updated yet.
         return tilingTransitionHandlerByDisplayId
             .get(taskInfo.displayId)
-            ?.moveTiledPairToFront(taskInfo, isTaskFocused = true) ?: false
+            ?.moveTiledPairToFront(taskInfo.taskId, isFocusedOnDisplay = true) ?: false
     }
 
     fun onOverviewAnimationStateChange(isRunning: Boolean) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 6f23233..666d4bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -35,11 +35,14 @@
 import android.window.WindowContainerTransaction
 import com.android.internal.annotations.VisibleForTesting
 import com.android.launcher3.icons.BaseIconFactory
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.FocusTransitionListener
 import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -49,6 +52,7 @@
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread
 import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.transition.FocusTransitionObserver
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.TRANSIT_MINIMIZE
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
@@ -78,13 +82,16 @@
     private val returnToDragStartAnimator: ReturnToDragStartAnimator,
     private val desktopUserRepositories: DesktopUserRepositories,
     private val desktopModeEventLogger: DesktopModeEventLogger,
+    private val focusTransitionObserver: FocusTransitionObserver,
+    @ShellMainThread private val mainExecutor: ShellExecutor,
     private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
 ) :
     Transitions.TransitionHandler,
     ShellTaskOrganizer.FocusListener,
     ShellTaskOrganizer.TaskVanishedListener,
     DragEventListener,
-    Transitions.TransitionObserver {
+    Transitions.TransitionObserver,
+    FocusTransitionListener {
     companion object {
         private val TAG: String = DesktopTilingWindowDecoration::class.java.simpleName
         private const val TILING_DIVIDER_TAG = "Tiling Divider"
@@ -176,8 +183,13 @@
             if (!isTilingManagerInitialised) {
                 desktopTilingDividerWindowManager = initTilingManagerForDisplay(displayId, config)
                 isTilingManagerInitialised = true
-                shellTaskOrganizer.addFocusListener(this)
-                isTilingFocused = true
+
+                if (Flags.enableDisplayFocusInShellTransitions()) {
+                    focusTransitionObserver.setLocalFocusTransitionListener(this, mainExecutor)
+                } else {
+                    shellTaskOrganizer.addFocusListener(this)
+                    isTilingFocused = true
+                }
             }
             leftTaskResizingHelper?.initIfNeeded()
             rightTaskResizingHelper?.initIfNeeded()
@@ -474,23 +486,33 @@
         }
     }
 
-    // Only called if [taskInfo] relates to a focused task
-    private fun isTilingFocusRemoved(taskInfo: RunningTaskInfo): Boolean {
+    // Only called if [taskId] relates to a focused task
+    private fun isTilingFocusRemoved(taskId: Int): Boolean {
         return isTilingFocused &&
-            taskInfo.taskId != leftTaskResizingHelper?.taskInfo?.taskId &&
-            taskInfo.taskId != rightTaskResizingHelper?.taskInfo?.taskId
+            taskId != leftTaskResizingHelper?.taskInfo?.taskId &&
+            taskId != rightTaskResizingHelper?.taskInfo?.taskId
     }
 
+    // Overriding ShellTaskOrganizer.FocusListener
     override fun onFocusTaskChanged(taskInfo: RunningTaskInfo?) {
+        if (Flags.enableDisplayFocusInShellTransitions()) return
         if (taskInfo != null) {
-            moveTiledPairToFront(taskInfo)
+            moveTiledPairToFront(taskInfo.taskId, taskInfo.isFocused)
         }
     }
 
+    // Overriding FocusTransitionListener
+    override fun onFocusedTaskChanged(taskId: Int,
+            isFocusedOnDisplay: Boolean,
+            isFocusedGlobally: Boolean) {
+        if (!Flags.enableDisplayFocusInShellTransitions()) return
+        moveTiledPairToFront(taskId, isFocusedOnDisplay)
+    }
+
     // Only called if [taskInfo] relates to a focused task
-    private fun isTilingRefocused(taskInfo: RunningTaskInfo): Boolean {
-        return taskInfo.taskId == leftTaskResizingHelper?.taskInfo?.taskId ||
-                taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId
+    private fun isTilingRefocused(taskId: Int): Boolean {
+        return taskId == leftTaskResizingHelper?.taskInfo?.taskId ||
+                taskId == rightTaskResizingHelper?.taskInfo?.taskId
     }
 
     private fun buildTiledTasksMoveToFront(leftOnTop: Boolean): WindowContainerTransaction {
@@ -582,14 +604,13 @@
      * If specified, [isTaskFocused] will override [RunningTaskInfo.isFocused]. This is to be used
      * when called when the task will be focused, but the [taskInfo] hasn't been updated yet.
      */
-    fun moveTiledPairToFront(taskInfo: RunningTaskInfo, isTaskFocused: Boolean? = null): Boolean {
+    fun moveTiledPairToFront(taskId: Int, isFocusedOnDisplay: Boolean): Boolean {
         if (!isTilingManagerInitialised) return false
 
-        val isFocused = isTaskFocused ?: taskInfo.isFocused
-        if (!isFocused) return false
+        if (!isFocusedOnDisplay) return false
 
         // If a task that isn't tiled is being focused, let the generic handler do the work.
-        if (isTilingFocusRemoved(taskInfo)) {
+        if (!Flags.enableDisplayFocusInShellTransitions() && isTilingFocusRemoved(taskId)) {
             isTilingFocused = false
             return false
         }
@@ -597,31 +618,29 @@
         val leftTiledTask = leftTaskResizingHelper ?: return false
         val rightTiledTask = rightTaskResizingHelper ?: return false
         if (!allTiledTasksVisible()) return false
-        val isLeftOnTop = taskInfo.taskId == leftTiledTask.taskInfo.taskId
-        if (isTilingRefocused(taskInfo)) {
-            val t = transactionSupplier.get()
-            isTilingFocused = true
-            if (taskInfo.taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
-                desktopTilingDividerWindowManager?.onRelativeLeashChanged(
-                    leftTiledTask.getLeash(),
-                    t,
-                )
-            }
-            if (taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
-                desktopTilingDividerWindowManager?.onRelativeLeashChanged(
-                    rightTiledTask.getLeash(),
-                    t,
-                )
-            }
-            transitions.startTransition(
-                TRANSIT_TO_FRONT,
-                buildTiledTasksMoveToFront(isLeftOnTop),
-                null,
-            )
-            t.apply()
-            return true
+        val isLeftOnTop = taskId == leftTiledTask.taskInfo.taskId
+        if (!isTilingRefocused(taskId)) return false
+        val t = transactionSupplier.get()
+        if (!Flags.enableDisplayFocusInShellTransitions()) isTilingFocused = true
+        if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
+          desktopTilingDividerWindowManager?.onRelativeLeashChanged(
+              leftTiledTask.getLeash(),
+              t,
+          )
         }
-        return false
+        if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
+          desktopTilingDividerWindowManager?.onRelativeLeashChanged(
+              rightTiledTask.getLeash(),
+              t,
+          )
+        }
+        transitions.startTransition(
+            TRANSIT_TO_FRONT,
+            buildTiledTasksMoveToFront(isLeftOnTop),
+            null,
+        )
+        t.apply()
+        return true
     }
 
     private fun allTiledTasksVisible(): Boolean {
@@ -706,7 +725,13 @@
     }
 
     private fun tearDownTiling() {
-        if (isTilingManagerInitialised) shellTaskOrganizer.removeFocusListener(this)
+        if (isTilingManagerInitialised) {
+            if (Flags.enableDisplayFocusInShellTransitions()) {
+                focusTransitionObserver.unsetLocalFocusTransitionListener(this)
+            } else {
+                shellTaskOrganizer.removeFocusListener(this)
+            }
+        }
 
         if (leftTaskResizingHelper == null && rightTaskResizingHelper == null) {
             shellTaskOrganizer.removeTaskVanishedListener(this)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index dc4fa37..db12f89 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -27,10 +27,13 @@
 import android.graphics.drawable.RippleDrawable
 import android.graphics.drawable.ShapeDrawable
 import android.graphics.drawable.shapes.RoundRectShape
+import android.os.Bundle
 import android.view.View
 import android.view.View.OnLongClickListener
 import android.view.ViewTreeObserver.OnGlobalLayoutListener
 import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
 import android.widget.ImageButton
 import android.widget.ImageView
 import android.widget.TextView
@@ -47,9 +50,10 @@
 import com.android.internal.R.color.materialColorSurfaceContainerLow
 import com.android.internal.R.color.materialColorSurfaceDim
 import com.android.window.flags.Flags
-import com.android.window.flags.Flags.enableMinimizeButton
 import com.android.wm.shell.R
 import android.window.DesktopModeFlags
+import androidx.core.view.ViewCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
 import com.android.wm.shell.windowdecor.MaximizeButtonView
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
 import com.android.wm.shell.windowdecor.common.OPACITY_100
@@ -72,7 +76,10 @@
         onCaptionButtonClickListener: View.OnClickListener,
         private val onLongClickListener: OnLongClickListener,
         onCaptionGenericMotionListener: View.OnGenericMotionListener,
-        onMaximizeHoverAnimationFinishedListener: () -> Unit
+        mOnLeftSnapClickListener: () -> Unit,
+        mOnRightSnapClickListener: () -> Unit,
+        mOnMaximizeOrRestoreClickListener: () -> Unit,
+        onMaximizeHoverAnimationFinishedListener: () -> Unit,
 ) : WindowDecorationViewHolder<AppHeaderViewHolder.HeaderData>(rootView) {
 
     data class HeaderData(
@@ -154,6 +161,91 @@
         minimizeWindowButton.setOnTouchListener(onCaptionTouchListener)
         maximizeButtonView.onHoverAnimationFinishedListener =
                 onMaximizeHoverAnimationFinishedListener
+
+        val a11yActionSnapLeft = AccessibilityAction(
+            R.id.action_snap_left,
+            context.resources.getString(R.string.desktop_mode_a11y_action_snap_left)
+        )
+        val a11yActionSnapRight = AccessibilityAction(
+            R.id.action_snap_right,
+            context.resources.getString(R.string.desktop_mode_a11y_action_snap_right)
+        )
+        val a11yActionMaximizeRestore = AccessibilityAction(
+            R.id.action_maximize_restore,
+            context.resources.getString(R.string.desktop_mode_a11y_action_maximize_restore)
+        )
+
+        captionHandle.accessibilityDelegate = object : View.AccessibilityDelegate() {
+            override fun onInitializeAccessibilityNodeInfo(
+                host: View,
+                info: AccessibilityNodeInfo
+            ) {
+                super.onInitializeAccessibilityNodeInfo(host, info)
+                info.addAction(a11yActionSnapLeft)
+                info.addAction(a11yActionSnapRight)
+                info.addAction(a11yActionMaximizeRestore)
+            }
+
+            override fun performAccessibilityAction(
+                host: View,
+                action: Int,
+                args: Bundle?
+            ): Boolean {
+                when (action) {
+                    R.id.action_snap_left -> mOnLeftSnapClickListener.invoke()
+                    R.id.action_snap_right -> mOnRightSnapClickListener.invoke()
+                    R.id.action_maximize_restore -> mOnMaximizeOrRestoreClickListener.invoke()
+                }
+
+                return super.performAccessibilityAction(host, action, args)
+            }
+        }
+        maximizeWindowButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
+            override fun onInitializeAccessibilityNodeInfo(
+                host: View,
+                info: AccessibilityNodeInfo
+            ) {
+                super.onInitializeAccessibilityNodeInfo(host, info)
+                info.addAction(AccessibilityAction.ACTION_CLICK)
+                info.addAction(a11yActionSnapLeft)
+                info.addAction(a11yActionSnapRight)
+                info.addAction(a11yActionMaximizeRestore)
+                host.isClickable = true
+            }
+
+            override fun performAccessibilityAction(
+                host: View,
+                action: Int,
+                args: Bundle?
+            ): Boolean {
+                when (action) {
+                    AccessibilityAction.ACTION_CLICK.id -> host.performClick()
+                    R.id.action_snap_left -> mOnLeftSnapClickListener.invoke()
+                    R.id.action_snap_right -> mOnRightSnapClickListener.invoke()
+                    R.id.action_maximize_restore -> mOnMaximizeOrRestoreClickListener.invoke()
+                }
+
+                return super.performAccessibilityAction(host, action, args)
+            }
+        }
+
+        with(context.resources) {
+            // Update a11y read out to say "double tap to maximize or restore window size"
+            ViewCompat.replaceAccessibilityAction(
+                maximizeWindowButton,
+                AccessibilityActionCompat.ACTION_CLICK,
+                getString(R.string.maximize_button_talkback_action_maximize_restore_text),
+                null
+            )
+
+            // Update a11y read out to say "double tap to minimize app window"
+            ViewCompat.replaceAccessibilityAction(
+                minimizeWindowButton,
+                AccessibilityActionCompat.ACTION_CLICK,
+                getString(R.string.minimize_button_talkback_action_maximize_restore_text),
+                null
+            )
+        }
     }
 
     override fun bindData(data: HeaderData) {
@@ -226,7 +318,7 @@
             minimizeWindowButton.background = getDrawable(1)
         }
         maximizeButtonView.setAnimationTints(isDarkMode())
-        minimizeWindowButton.isGone = !enableMinimizeButton()
+        minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()
     }
 
     private fun bindDataWithThemedHeaders(
@@ -276,7 +368,7 @@
                 drawableInsets = minimizeDrawableInsets
             )
         }
-        minimizeWindowButton.isGone = !enableMinimizeButton()
+        minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()
         // Maximize button.
         maximizeButtonView.apply {
             setAnimationTints(
@@ -329,11 +421,6 @@
     }
 
     fun runOnAppChipGlobalLayout(runnable: () -> Unit) {
-        if (openMenuButton.isAttachedToWindow) {
-            // App chip is already inflated.
-            runnable()
-            return
-        }
         // Wait for app chip to be inflated before notifying repository.
         openMenuButton.viewTreeObserver.addOnGlobalLayoutListener(object :
             OnGlobalLayoutListener {
@@ -634,6 +721,9 @@
             onCaptionButtonClickListener: View.OnClickListener,
             onLongClickListener: OnLongClickListener,
             onCaptionGenericMotionListener: View.OnGenericMotionListener,
+            mOnLeftSnapClickListener: () -> Unit,
+            mOnRightSnapClickListener: () -> Unit,
+            mOnMaximizeOrRestoreClickListener: () -> Unit,
             onMaximizeHoverAnimationFinishedListener: () -> Unit,
         ): AppHeaderViewHolder = AppHeaderViewHolder(
             rootView,
@@ -641,6 +731,9 @@
             onCaptionButtonClickListener,
             onLongClickListener,
             onCaptionGenericMotionListener,
+            mOnLeftSnapClickListener,
+            mOnRightSnapClickListener,
+            mOnMaximizeOrRestoreClickListener,
             onMaximizeHoverAnimationFinishedListener,
         )
     }
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt
new file mode 100644
index 0000000..6b159a4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 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.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromAllAppsLandscape : OpenAppFromAllApps(rotation = ROTATION_90) {
+
+    @ExpectedScenarios(["CASCADE_APP"])
+    @Test
+    override fun openApp() = super.openApp()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt
new file mode 100644
index 0000000..07b4392
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 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.flicker
+
+import android.tools.Rotation.ROTATION_0
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromAllAppsPortrait : OpenAppFromAllApps(rotation = ROTATION_0) {
+
+    @ExpectedScenarios(["CASCADE_APP"])
+    @Test
+    override fun openApp() = super.openApp()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt
new file mode 100644
index 0000000..caadd3b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 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.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromTaskbarLandscape : OpenAppFromTaskbar(rotation = ROTATION_90) {
+
+    @ExpectedScenarios(["CASCADE_APP"])
+    @Test
+    override fun openApp() = super.openApp()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt
new file mode 100644
index 0000000..77f5ab2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 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.flicker
+
+import android.tools.Rotation.ROTATION_0
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromTaskbarPortrait : OpenAppFromTaskbar(rotation = ROTATION_0) {
+
+    @ExpectedScenarios(["CASCADE_APP"])
+    @Test
+    override fun openApp() = super.openApp()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
index c3abf23..ff0a9d5 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
@@ -34,18 +34,6 @@
     @Test
     override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
 
-    @ExpectedScenarios(["EDGE_RESIZE"])
-    @Test
-    override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
-
-    @ExpectedScenarios(["EDGE_RESIZE"])
-    @Test
-    override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
-
-    @ExpectedScenarios(["EDGE_RESIZE"])
-    @Test
-    override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
-
     companion object {
         @JvmStatic
         @FlickerConfigProvider
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
index 86b0e6f..73a4753 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
@@ -34,18 +34,6 @@
     @Test
     override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
 
-    @ExpectedScenarios(["EDGE_RESIZE"])
-    @Test
-    override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
-
-    @ExpectedScenarios(["EDGE_RESIZE"])
-    @Test
-    override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
-
-    @ExpectedScenarios(["EDGE_RESIZE"])
-    @Test
-    override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
-
     companion object {
         @JvmStatic
         @FlickerConfigProvider
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
index e6bb9ef..f81c161 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
@@ -34,18 +34,6 @@
     @Test
     override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
 
-    @ExpectedScenarios(["EDGE_RESIZE"])
-    @Test
-    override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
-
-    @ExpectedScenarios(["EDGE_RESIZE"])
-    @Test
-    override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
-
-    @ExpectedScenarios(["EDGE_RESIZE"])
-    @Test
-    override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
-
     companion object {
         @JvmStatic
         @FlickerConfigProvider
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
index 966aea3..7855698 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
@@ -21,6 +21,7 @@
 import android.tools.Rotation
 import android.tools.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.traces.parsers.WindowManagerStateHelper
+import android.window.DesktopModeFlags
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
@@ -59,7 +60,7 @@
     fun setup() {
         Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
         if (usingKeyboard) {
-            Assume.assumeTrue(Flags.enableTaskResizingKeyboardShortcuts())
+            Assume.assumeTrue(DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue)
         }
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
index 46c97b0..2f99fba 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
@@ -21,6 +21,7 @@
 import android.tools.Rotation
 import android.tools.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.traces.parsers.WindowManagerStateHelper
+import android.window.DesktopModeFlags
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
@@ -62,7 +63,7 @@
         Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
         Assume.assumeTrue(Flags.enableMinimizeButton())
         if (usingKeyboard) {
-            Assume.assumeTrue(Flags.enableTaskResizingKeyboardShortcuts())
+            Assume.assumeTrue(DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue)
         }
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt
index 36cdd5b..3482196 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt
@@ -20,12 +20,12 @@
 import android.tools.NavBar
 import android.tools.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
 import android.tools.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
 import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
-import com.android.server.wm.flicker.helpers.MailAppHelper
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.window.flags.Flags
 import com.android.wm.shell.Utils
@@ -44,7 +44,7 @@
     private val wmHelper = WindowManagerStateHelper(instrumentation)
     private val device = UiDevice.getInstance(instrumentation)
     private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
-    private val mailApp = MailAppHelper(instrumentation)
+    private val calculatorApp = CalculatorAppHelper(instrumentation)
 
     @Rule
     @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
@@ -64,13 +64,13 @@
     open fun openApp() {
         tapl.launchedAppState.taskbar
             .openAllApps()
-            .getAppIcon(mailApp.appName)
-            .launch(mailApp.packageName)
+            .getAppIcon(calculatorApp.appName)
+            .launch(calculatorApp.packageName)
     }
 
     @After
     fun teardown() {
-        mailApp.exit(wmHelper)
+        calculatorApp.exit(wmHelper)
         testApp.exit(wmHelper)
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
index f198cfe..1dfa6ac 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
@@ -72,33 +72,6 @@
         )
     }
 
-    @Test
-    open fun resizeAppWithEdgeResizeLeft() {
-        testApp.edgeResize(
-            wmHelper,
-            motionEventHelper,
-            DesktopModeAppHelper.Edges.LEFT
-        )
-    }
-
-    @Test
-    open fun resizeAppWithEdgeResizeTop() {
-        testApp.edgeResize(
-            wmHelper,
-            motionEventHelper,
-            DesktopModeAppHelper.Edges.TOP
-        )
-    }
-
-    @Test
-    open fun resizeAppWithEdgeResizeBottom() {
-        testApp.edgeResize(
-            wmHelper,
-            motionEventHelper,
-            DesktopModeAppHelper.Edges.BOTTOM
-        )
-    }
-
     @After
     fun teardown() {
         testApp.exit(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt
index 0680644..59d15ca 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt
@@ -20,6 +20,7 @@
 import android.tools.NavBar
 import android.tools.Rotation
 import android.tools.traces.parsers.WindowManagerStateHelper
+import android.window.DesktopModeFlags
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
@@ -59,7 +60,7 @@
     @Before
     fun setup() {
         Assume.assumeTrue(Flags.enableDesktopWindowingMode() &&
-                Flags.enableTaskResizingKeyboardShortcuts() && tapl.isTablet)
+                DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue && tapl.isTablet)
         testApp.enterDesktopMode(wmHelper, device)
     }
 
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
index 31d89f9..9d501d3 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
@@ -23,8 +23,8 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.RecentTasksUtils
 import com.android.wm.shell.Utils
-import com.android.wm.shell.flicker.utils.RecentTasksUtils
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
index 1af6cac..f574f02 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
@@ -23,8 +23,8 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.RecentTasksUtils
 import com.android.wm.shell.Utils
-import com.android.wm.shell.flicker.utils.RecentTasksUtils
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
index 8ad8c7b..60fcce2 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
@@ -23,8 +23,8 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.RecentTasksUtils
 import com.android.wm.shell.Utils
-import com.android.wm.shell.flicker.utils.RecentTasksUtils
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
index da0ace4..e6a080b 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
@@ -23,8 +23,8 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.RecentTasksUtils
 import com.android.wm.shell.Utils
-import com.android.wm.shell.flicker.utils.RecentTasksUtils
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.After
 import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index b5b7847..80e4c47a 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.RequiresFlagsDisabled
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
@@ -51,6 +52,7 @@
  *        apps are running before setup
  * ```
  */
+@FlakyTest(bugId = 391734110)
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
index f9c60ad..aa893ed 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
@@ -19,7 +19,6 @@
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.platform.test.annotations.RequiresDevice
-import android.platform.test.annotations.RequiresFlagsDisabled
 import android.tools.Rotation
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
@@ -29,7 +28,6 @@
 import android.tools.traces.parsers.toFlickerComponent
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.Flags
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.Assume
 import org.junit.FixMethodOrder
@@ -64,7 +62,6 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
 open class FromSplitScreenAutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
     AutoEnterPipOnGoToHomeTest(flicker) {
     private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
index 805f4c2..8e7cb56 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
@@ -19,7 +19,6 @@
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.platform.test.annotations.RequiresDevice
-import android.platform.test.annotations.RequiresFlagsDisabled
 import android.tools.Rotation
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
@@ -30,7 +29,6 @@
 import com.android.server.wm.flicker.helpers.PipAppHelper
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.Flags
 import com.android.wm.shell.flicker.pip.common.EnterPipTransition
 import com.android.wm.shell.flicker.utils.SplitScreenUtils
 import org.junit.Assume
@@ -66,7 +64,6 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
 @FlakyTest(bugId = 386333280)
 open class FromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) :
     EnterPipTransition(flicker) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
index 40b685c..4972fa9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
@@ -23,6 +23,9 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.display.DisplayManager;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.TestableContext;
 
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -31,6 +34,8 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
 import org.mockito.MockitoAnnotations;
 
 /**
@@ -38,6 +43,16 @@
  */
 public abstract class ShellTestCase {
 
+    @ClassRule
+    public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
+
     protected TestableContext mContext;
     private PackageManager mPm;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index bba9418..94dc774 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -41,7 +41,6 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.window.TransitionInfo;
@@ -55,7 +54,6 @@
 import com.google.testing.junit.testparameterinjector.TestParameterInjector;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -73,9 +71,6 @@
 @RunWith(TestParameterInjector.class)
 public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnimationTestBase {
 
-    @Rule
-    public SetFlagsRule mRule = new SetFlagsRule();
-
     @Before
     public void setup() {
         super.setUp();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index 39d5507..9f29ef7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -34,7 +34,6 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
@@ -46,7 +45,6 @@
 import com.android.wm.shell.transition.TransitionInfoBuilder;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -64,9 +62,6 @@
     private static final Rect EMBEDDED_LEFT_BOUNDS = new Rect(0, 0, 500, 500);
     private static final Rect EMBEDDED_RIGHT_BOUNDS = new Rect(500, 0, 1000, 500);
 
-    @Rule
-    public SetFlagsRule mRule = new SetFlagsRule();
-
     @Before
     public void setup() {
         super.setUp();
@@ -276,7 +271,9 @@
         mController.startAnimation(mTransition, info, mStartTransaction,
                 mFinishTransaction, mFinishCallback);
         verify(mFinishCallback, never()).onTransitionFinished(any());
-        mController.mergeAnimation(mTransition, info, new SurfaceControl.Transaction(),
+        mController.mergeAnimation(mTransition, info,
+                new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(),
                 mTransition, (wct) -> {});
         verify(mFinishCallback).onTransitionFinished(any());
     }
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 bbdb90f..05750a5 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
@@ -60,7 +60,6 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.IRemoteAnimationRunner;
@@ -91,7 +90,6 @@
 import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -152,9 +150,6 @@
 
     private BackAnimationController.BackTransitionHandler mBackTransitionHandler;
 
-    @Rule
-    public SetFlagsRule mSetflagsRule = new SetFlagsRule();
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -671,7 +666,7 @@
         Transitions.TransitionFinishCallback mergeCallback =
                 mock(Transitions.TransitionFinishCallback.class);
         mBackTransitionHandler.mergeAnimation(
-                mock(IBinder.class), tInfo2, st, mock(IBinder.class), mergeCallback);
+                mock(IBinder.class), tInfo2, st, ft, mock(IBinder.class), mergeCallback);
         mBackTransitionHandler.onAnimationFinished();
         verify(callback).onTransitionFinished(any());
         verify(mergeCallback).onTransitionFinished(any());
@@ -706,7 +701,7 @@
         mBackTransitionHandler.mClosePrepareTransition = mock(IBinder.class);
         mergeCallback = mock(Transitions.TransitionFinishCallback.class);
         mBackTransitionHandler.mergeAnimation(mBackTransitionHandler.mClosePrepareTransition,
-                tInfo2, st, mock(IBinder.class), mergeCallback);
+                tInfo2, st, ft, mock(IBinder.class), mergeCallback);
         assertTrue("Change should be consumed", tInfo2.getChanges().isEmpty());
         verify(callback).onTransitionFinished(any());
     }
@@ -752,7 +747,7 @@
         final TransitionInfo closeInfo = createTransitionInfo(TRANSIT_CLOSE, close);
         Transitions.TransitionFinishCallback mergeCallback =
                 mock(Transitions.TransitionFinishCallback.class);
-        mBackTransitionHandler.mergeAnimation(mock(IBinder.class), closeInfo, ft,
+        mBackTransitionHandler.mergeAnimation(mock(IBinder.class), closeInfo, st, ft,
                 mock(IBinder.class), mergeCallback);
         verify(callback2).onTransitionFinished(any());
         verify(mergeCallback, never()).onTransitionFinished(any());
@@ -771,7 +766,7 @@
                 openTaskId2, TRANSIT_OPEN, FLAG_MOVED_TO_TOP);
         final TransitionInfo openInfo = createTransitionInfo(TRANSIT_OPEN, open2, close);
         mergeCallback = mock(Transitions.TransitionFinishCallback.class);
-        mBackTransitionHandler.mergeAnimation(mock(IBinder.class), openInfo, ft,
+        mBackTransitionHandler.mergeAnimation(mock(IBinder.class), openInfo, st, ft,
                 mock(IBinder.class), mergeCallback);
         verify(callback3).onTransitionFinished(any());
         verify(mergeCallback, never()).onTransitionFinished(any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 6d7a18d..2ef6c55 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -32,6 +32,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.wm.shell.ShellTestCase;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,7 +44,7 @@
 @SmallTest
 @TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner.class)
-public class BackProgressAnimatorTest {
+public class BackProgressAnimatorTest extends ShellTestCase {
     private BackProgressAnimator mProgressAnimator;
     private BackEvent mReceivedBackEvent;
     private float mTargetProgress = 0.5f;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
new file mode 100644
index 0000000..9d0ddbc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2025 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.bubbles;
+
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+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.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.view.ViewRootImpl;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestSyncExecutor;
+import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
+import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.taskview.TaskView;
+import com.android.wm.shell.taskview.TaskViewRepository;
+import com.android.wm.shell.taskview.TaskViewTaskController;
+import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.Transitions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests of {@link BubbleTransitions}.
+ */
+@SmallTest
+public class BubbleTransitionsTest extends ShellTestCase {
+    @Mock
+    private BubbleData mBubbleData;
+    @Mock
+    private Bubble mBubble;
+    @Mock
+    private Transitions mTransitions;
+    @Mock
+    private SyncTransactionQueue mSyncQueue;
+    @Mock
+    private BubbleExpandedViewManager mExpandedViewManager;
+    @Mock
+    private BubblePositioner mBubblePositioner;
+    @Mock
+    private BubbleLogger mBubbleLogger;
+    @Mock
+    private BubbleStackView mStackView;
+    @Mock
+    private BubbleBarLayerView mLayerView;
+    @Mock
+    private BubbleIconFactory mIconFactory;
+
+    @Mock private ShellTaskOrganizer mTaskOrganizer;
+    private TaskViewTransitions mTaskViewTransitions;
+    private TaskViewRepository mRepository;
+    private BubbleTransitions mBubbleTransitions;
+    private BubbleTaskViewFactory mTaskViewFactory;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mRepository = new TaskViewRepository();
+        ShellExecutor syncExecutor = new TestSyncExecutor();
+
+        when(mTransitions.getMainExecutor()).thenReturn(syncExecutor);
+        when(mTransitions.isRegistered()).thenReturn(true);
+        mTaskViewTransitions = new TaskViewTransitions(mTransitions, mRepository, mTaskOrganizer,
+                mSyncQueue);
+        mBubbleTransitions = new BubbleTransitions(mTransitions, mTaskOrganizer, mRepository,
+                mBubbleData, mTaskViewTransitions, mContext);
+        mTaskViewFactory = () -> {
+            TaskViewTaskController taskViewTaskController = new TaskViewTaskController(
+                    mContext, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
+            TaskView taskView = new TaskView(mContext, mTaskViewTransitions,
+                    taskViewTaskController);
+            return new BubbleTaskView(taskView, syncExecutor);
+        };
+        final BubbleBarExpandedView bbev = mock(BubbleBarExpandedView.class);
+        final ViewRootImpl vri = mock(ViewRootImpl.class);
+        when(bbev.getViewRootImpl()).thenReturn(vri);
+        when(mBubble.getBubbleBarExpandedView()).thenReturn(bbev);
+    }
+
+    private ActivityManager.RunningTaskInfo setupBubble() {
+        ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+        final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
+        final IBinder asBinder = mock(IBinder.class);
+        when(itoken.asBinder()).thenReturn(asBinder);
+        WindowContainerToken token = new WindowContainerToken(itoken);
+        taskInfo.token = token;
+        final TaskView tv = mock(TaskView.class);
+        final TaskViewTaskController tvtc = mock(TaskViewTaskController.class);
+        when(tvtc.getTaskInfo()).thenReturn(taskInfo);
+        when(tv.getController()).thenReturn(tvtc);
+        when(mBubble.getTaskView()).thenReturn(tv);
+        mRepository.add(tvtc);
+        return taskInfo;
+    }
+
+    @Test
+    public void testConvertToBubble() {
+        // Basic walk-through of convert-to-bubble transition stages
+        ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+        final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertToBubble(
+                mBubble, taskInfo, mExpandedViewManager, mTaskViewFactory, mBubblePositioner,
+                mBubbleLogger, mStackView, mLayerView, mIconFactory, false);
+        final BubbleTransitions.ConvertToBubble ctb = (BubbleTransitions.ConvertToBubble) bt;
+        ctb.onInflated(mBubble);
+        when(mLayerView.canExpandView(any())).thenReturn(true);
+        verify(mTransitions).startTransition(anyInt(), any(), eq(ctb));
+        verify(mBubble).setPreparingTransition(eq(bt));
+        // Ensure we are communicating with the taskviewtransitions queue
+        assertTrue(mTaskViewTransitions.hasPending());
+
+        final TransitionInfo info = new TransitionInfo(TRANSIT_CHANGE, 0);
+        final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
+                mock(SurfaceControl.class));
+        chg.setTaskInfo(taskInfo);
+        chg.setMode(TRANSIT_CHANGE);
+        info.addChange(chg);
+        info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+        SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+        SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+        final boolean[] finishCalled = new boolean[]{false};
+        Transitions.TransitionFinishCallback finishCb = wct -> {
+            assertFalse(finishCalled[0]);
+            finishCalled[0] = true;
+        };
+        ctb.startAnimation(ctb.mTransition, info, startT, finishT, finishCb);
+        assertFalse(mTaskViewTransitions.hasPending());
+
+        verify(mBubbleData).notificationEntryUpdated(eq(mBubble), anyBoolean(), anyBoolean());
+        ctb.continueExpand();
+
+        clearInvocations(mBubble);
+        verify(mBubble, never()).setPreparingTransition(any());
+
+        ctb.surfaceCreated();
+        verify(mBubble).setPreparingTransition(isNull());
+        ArgumentCaptor<Runnable> animCb = ArgumentCaptor.forClass(Runnable.class);
+        verify(mLayerView).animateConvert(any(), any(), any(), any(), animCb.capture());
+        assertFalse(finishCalled[0]);
+        animCb.getValue().run();
+        assertTrue(finishCalled[0]);
+    }
+
+    @Test
+    public void testConvertFromBubble() {
+        ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+        final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertFromBubble(
+                mBubble, taskInfo);
+        final BubbleTransitions.ConvertFromBubble cfb = (BubbleTransitions.ConvertFromBubble) bt;
+        verify(mTransitions).startTransition(anyInt(), any(), eq(cfb));
+        verify(mBubble).setPreparingTransition(eq(bt));
+        assertTrue(mTaskViewTransitions.hasPending());
+
+        final TransitionInfo info = new TransitionInfo(TRANSIT_CHANGE, 0);
+        final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
+                mock(SurfaceControl.class));
+        chg.setMode(TRANSIT_CHANGE);
+        chg.setTaskInfo(taskInfo);
+        info.addChange(chg);
+        info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+        SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+        SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+        Transitions.TransitionFinishCallback finishCb = wct -> {};
+        cfb.startAnimation(cfb.mTransition, info, startT, finishT, finishCb);
+
+        // Can really only verify that it interfaces with the taskViewTransitions queue.
+        // The actual functioning of this is tightly-coupled with SurfaceFlinger and renderthread
+        // in order to properly synchronize surface manipulation with drawing and thus can't be
+        // directly tested.
+        assertFalse(mTaskViewTransitions.hasPending());
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
index f8eb50b..622e4cb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
@@ -38,6 +38,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.transition.TransitionInfoBuilder;
 
 import org.junit.Before;
@@ -49,7 +50,7 @@
  * Tests of {@link BubblesTransitionObserver}.
  */
 @SmallTest
-public class BubblesTransitionObserverTest {
+public class BubblesTransitionObserverTest extends ShellTestCase {
 
     @Mock
     private BubbleController mBubbleController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
index f8ee300..3323740 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
@@ -29,6 +29,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
@@ -41,7 +42,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class DevicePostureControllerTest {
+public class DevicePostureControllerTest extends ShellTestCase {
     @Mock
     private Context mContext;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 6f3a3ec..ee9d177 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -39,8 +39,6 @@
 import android.os.Looper;
 import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.view.IWindowManager;
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
@@ -55,7 +53,6 @@
 import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -70,9 +67,6 @@
  */
 @SmallTest
 public class DisplayImeControllerTest extends ShellTestCase {
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
     @Mock
     private SurfaceControl.Transaction mT;
     @Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
index bec91e9..6b0c390 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
@@ -33,8 +33,6 @@
 import org.mockito.ArgumentMatchers
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.kotlin.any
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.doThrow
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.verify
@@ -80,17 +78,19 @@
     @Test
     fun supportsMultiInstanceSplit_inStaticAllowList() {
         val allowList = arrayOf(TEST_PACKAGE)
-        val helper = MultiInstanceHelper(mContext, context.packageManager, allowList, true)
+        val helper = MultiInstanceHelper(mContext, context.packageManager, allowList,
+            mock(), mock(), true)
         val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
-        assertEquals(true, helper.supportsMultiInstanceSplit(component))
+        assertEquals(true, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
     }
 
     @Test
     fun supportsMultiInstanceSplit_notInStaticAllowList() {
         val allowList = arrayOf(TEST_PACKAGE)
-        val helper = MultiInstanceHelper(mContext, context.packageManager, allowList, true)
+        val helper = MultiInstanceHelper(mContext, context.packageManager, allowList,
+            mock(), mock(), true)
         val component = ComponentName(TEST_NOT_ALLOWED_PACKAGE, TEST_ACTIVITY)
-        assertEquals(false, helper.supportsMultiInstanceSplit(component))
+        assertEquals(false, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
     }
 
     @Test
@@ -99,17 +99,17 @@
         val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
         val pm = mock<PackageManager>()
         val activityProp = PackageManager.Property("", true, "", "")
-        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-            eq(component)))
+        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER_ID)))
                 .thenReturn(activityProp)
         val appProp = PackageManager.Property("", false, "", "")
-        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-            eq(component.packageName)))
+        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName), eq(null), eq(TEST_OTHER_USER_ID)))
                 .thenReturn(appProp)
 
-        val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
+        val helper = MultiInstanceHelper(mContext, pm, emptyArray(), mock(), mock(), true)
         // Expect activity property to override application property
-        assertEquals(true, helper.supportsMultiInstanceSplit(component))
+        assertEquals(true, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
     }
 
     @Test
@@ -118,17 +118,17 @@
         val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
         val pm = mock<PackageManager>()
         val activityProp = PackageManager.Property("", false, "", "")
-        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-            eq(component)))
+        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER_ID)))
                 .thenReturn(activityProp)
         val appProp = PackageManager.Property("", true, "", "")
-        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-            eq(component.packageName)))
+        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName), eq(null), eq(TEST_OTHER_USER_ID)))
                 .thenReturn(appProp)
 
-        val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
+        val helper = MultiInstanceHelper(mContext, pm, emptyArray(), mock(), mock(), true)
         // Expect activity property to override application property
-        assertEquals(false, helper.supportsMultiInstanceSplit(component))
+        assertEquals(false, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
     }
 
     @Test
@@ -136,17 +136,17 @@
     fun supportsMultiInstanceSplit_noActivityPropertyApplicationPropertyTrue() {
         val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
         val pm = mock<PackageManager>()
-        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-            eq(component)))
+        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER_ID)))
                 .thenThrow(PackageManager.NameNotFoundException())
         val appProp = PackageManager.Property("", true, "", "")
-        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-            eq(component.packageName)))
+        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName), eq(null), eq(TEST_OTHER_USER_ID)))
                 .thenReturn(appProp)
 
-        val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
+        val helper = MultiInstanceHelper(mContext, pm, emptyArray(), mock(), mock(), true)
         // Expect fall through to app property
-        assertEquals(true, helper.supportsMultiInstanceSplit(component))
+        assertEquals(true, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
     }
 
     @Test
@@ -154,15 +154,15 @@
     fun supportsMultiInstanceSplit_noActivityOrAppProperty() {
         val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
         val pm = mock<PackageManager>()
-        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-            eq(component)))
+        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER_ID)))
                 .thenThrow(PackageManager.NameNotFoundException())
-        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-            eq(component.packageName)))
+        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName), eq(null), eq(TEST_OTHER_USER_ID)))
                 .thenThrow(PackageManager.NameNotFoundException())
 
-        val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
-        assertEquals(false, helper.supportsMultiInstanceSplit(component))
+        val helper = MultiInstanceHelper(mContext, pm, emptyArray(), mock(), mock(), true)
+        assertEquals(false, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
     }
 
     @Test
@@ -171,24 +171,25 @@
         val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
         val pm = mock<PackageManager>()
         val activityProp = PackageManager.Property("", true, "", "")
-        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-            eq(component)))
+        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER_ID)))
             .thenReturn(activityProp)
         val appProp = PackageManager.Property("", true, "", "")
-        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-            eq(component.packageName)))
+        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName), eq(null), eq(TEST_OTHER_USER_ID)))
             .thenReturn(appProp)
 
-        val helper = MultiInstanceHelper(mContext, pm, emptyArray(), false)
+        val helper = MultiInstanceHelper(mContext, pm, emptyArray(), mock(), mock(), false)
         // Expect we only check the static list and not the property
-        assertEquals(false, helper.supportsMultiInstanceSplit(component))
+        assertEquals(false, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
         verify(pm, never()).getProperty(any(), any<ComponentName>())
     }
 
     companion object {
         val TEST_PACKAGE = "com.android.wm.shell.common"
-        val TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.common.fake";
-        val TEST_ACTIVITY = "TestActivity";
+        val TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.common.fake"
+        val TEST_ACTIVITY = "TestActivity"
         val TEST_SHORTCUT_ID = "test_shortcut_1"
+        val TEST_OTHER_USER_ID = 1234
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/UserProfileContextsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/UserProfileContextsTest.kt
index ef0b8ab..56d4017 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/UserProfileContextsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/UserProfileContextsTest.kt
@@ -69,6 +69,7 @@
             }
             .whenever(baseContext)
             .createContextAsUser(any<UserHandle>(), anyInt())
+        doReturn(DEFAULT_USER).whenever(baseContext).userId
         // Define users and profiles
         val currentUser = ActivityManager.getCurrentUser()
         whenever(userManager.getProfiles(eq(currentUser)))
@@ -136,6 +137,25 @@
         assertThat(userProfilesContexts[SECOND_PROFILE]?.userId).isEqualTo(SECOND_PROFILE)
     }
 
+    @Test
+    fun onUserProfilesChanged_keepDefaultUser() {
+        val userChangeListener = retrieveUserChangeListener()
+        val newUserContext = createContextForUser(SECOND_USER)
+
+        userChangeListener.onUserChanged(SECOND_USER, newUserContext)
+        userChangeListener.onUserProfilesChanged(SECOND_PROFILES)
+
+        assertThat(userProfilesContexts[DEFAULT_USER]).isEqualTo(baseContext)
+    }
+
+    @Test
+    fun getOrCreate_newUser_shouldCreateTheUser() {
+        val newContext = userProfilesContexts.getOrCreate(SECOND_USER)
+
+        assertThat(newContext).isNotNull()
+        assertThat(userProfilesContexts[SECOND_USER]).isEqualTo(newContext)
+    }
+
     private fun retrieveUserChangeListener(): UserChangeListener {
         val captor = argumentCaptor<UserChangeListener>()
 
@@ -155,6 +175,7 @@
         const val MAIN_PROFILE = 11
         const val SECOND_PROFILE = 15
         const val SECOND_PROFILE_2 = 17
+        const val DEFAULT_USER = 25
 
         val SECOND_PROFILES =
             listOf(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
deleted file mode 100644
index 7157a7f..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.compatui
-
-import android.content.ComponentName
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.internal.R
-import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for [@link AppCompatUtils].
- *
- * Build/Install/Run: atest WMShellUnitTests:AppCompatUtilsTest
- */
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class AppCompatUtilsTest : CompatUIShellTestCase() {
-    @Test
-    fun testIsTopActivityExemptFromDesktopWindowing_onlyTransparentActivitiesInStack() {
-        assertTrue(isTopActivityExemptFromDesktopWindowing(mContext,
-            createFreeformTask(/* displayId */ 0)
-                    .apply {
-                        isActivityStackTransparent = true
-                        isTopActivityNoDisplay = false
-                        numActivities = 1
-                    }))
-    }
-
-    @Test
-    fun testIsTopActivityExemptFromDesktopWindowing_noActivitiesInStack() {
-        assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
-            createFreeformTask(/* displayId */ 0)
-                .apply {
-                    isActivityStackTransparent = true
-                    isTopActivityNoDisplay = false
-                    numActivities = 0
-                }))
-    }
-
-    @Test
-    fun testIsTopActivityExemptFromDesktopWindowing_nonTransparentActivitiesInStack() {
-        assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
-            createFreeformTask(/* displayId */ 0)
-                .apply {
-                    isActivityStackTransparent = false
-                    isTopActivityNoDisplay = false
-                    numActivities = 1
-                }))
-    }
-
-    @Test
-    fun testIsTopActivityExemptFromDesktopWindowing_transparentActivityStack_notDisplayed() {
-        assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
-            createFreeformTask(/* displayId */ 0)
-                .apply {
-                    isActivityStackTransparent = true
-                    isTopActivityNoDisplay = true
-                    numActivities = 1
-                }))
-    }
-
-    @Test
-    fun testIsTopActivityExemptFromDesktopWindowing_systemUiTask() {
-        val systemUIPackageName = context.resources.getString(R.string.config_systemUi)
-        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
-        assertTrue(isTopActivityExemptFromDesktopWindowing(mContext,
-            createFreeformTask(/* displayId */ 0)
-                    .apply {
-                        baseActivity = baseComponent
-                        isTopActivityNoDisplay = false
-                    }))
-    }
-
-    @Test
-    fun testIsTopActivityExemptFromDesktopWindowing_systemUiTask_notDisplayed() {
-        val systemUIPackageName = context.resources.getString(R.string.config_systemUi)
-        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
-        assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
-            createFreeformTask(/* displayId */ 0)
-                .apply {
-                    baseActivity = baseComponent
-                    isTopActivityNoDisplay = true
-                }))
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java
index 5a49d01..979cee9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java
@@ -16,24 +16,10 @@
 
 package com.android.wm.shell.compatui;
 
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.SetFlagsRule;
-
 import com.android.wm.shell.ShellTestCase;
 
-import org.junit.Rule;
-
 /**
  * Base class for CompatUI tests.
  */
 public class CompatUIShellTestCase extends ShellTestCase {
-
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index 61b6d80..010474e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -38,6 +38,7 @@
 import android.app.TaskInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.testing.AndroidTestingRunner;
 import android.util.Pair;
@@ -394,8 +395,8 @@
 
     @Test
     @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
+    @EnableFlags(Flags.FLAG_ALLOW_HIDE_SCM_BUTTON)
     public void testShouldShowSizeCompatRestartButton() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_HIDE_SCM_BUTTON);
         doReturn(85).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance();
         mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
                 mCallback, mTaskListener, mDisplayLayout, new CompatUIHintsState(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt
index 319122d..d3a2c9a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt
@@ -18,7 +18,6 @@
 
 
 import android.graphics.Point
-import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.testing.AndroidTestingRunner
 import android.view.View
 import androidx.test.filters.SmallTest
@@ -29,7 +28,6 @@
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNull
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -45,9 +43,6 @@
 
     lateinit var repository: CompatUIRepository
 
-    @get:Rule
-    val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
-
     @Before
     fun setUp() {
         repository = DefaultCompatUIRepository()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
index 78bb721..008c499 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
@@ -20,7 +20,6 @@
 import android.graphics.Rect
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CLOSE
@@ -36,14 +35,12 @@
 import com.android.wm.shell.util.TransitionObserverInputBuilder
 import com.android.wm.shell.util.executeTransitionObserverTest
 import java.util.function.Consumer
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
-import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
 
 /**
@@ -56,9 +53,6 @@
 @SmallTest
 class LetterboxTransitionObserverTest : ShellTestCase() {
 
-    @get:Rule
-    val setFlagsRule: SetFlagsRule = SetFlagsRule()
-
     @Test
     @DisableFlags(Flags.FLAG_APP_COMPAT_REFACTORING)
     fun `when initialized and flag disabled the observer is not registered`() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
index 957fdf99..09ffd94 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
@@ -25,7 +25,6 @@
 import android.os.Binder
 import android.os.UserManager
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.view.Display.DEFAULT_DISPLAY
 import android.window.WindowContainerTransaction
@@ -62,7 +61,6 @@
 import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -89,8 +87,6 @@
 @ExperimentalCoroutinesApi
 @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE)
 class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
     @Mock lateinit var testExecutor: ShellExecutor
     @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
     @Mock lateinit var transitions: Transitions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index 5d8d5a7..0d5741f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -16,12 +16,13 @@
 
 package com.android.wm.shell.desktopmode
 
+import android.app.ActivityManager.RunningTaskInfo
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
 import android.content.ContentResolver
 import android.os.Binder
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.provider.Settings
 import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
 import android.testing.AndroidTestingRunner
@@ -37,7 +38,9 @@
 import com.android.window.flags.Flags
 import com.android.wm.shell.MockToken
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
 import com.android.wm.shell.common.ShellExecutor
@@ -47,7 +50,6 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.isNull
@@ -70,9 +72,6 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class DesktopDisplayEventHandlerTest : ShellTestCase() {
-
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
     @Mock lateinit var testExecutor: ShellExecutor
     @Mock lateinit var transitions: Transitions
     @Mock lateinit var displayController: DisplayController
@@ -81,12 +80,20 @@
     @Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories
     @Mock private lateinit var mockDesktopRepository: DesktopRepository
     @Mock private lateinit var mockDesktopTasksController: DesktopTasksController
+    @Mock private lateinit var shellTaskOrganizer: ShellTaskOrganizer
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var shellInit: ShellInit
     private lateinit var handler: DesktopDisplayEventHandler
 
     private val onDisplaysChangedListenerCaptor = argumentCaptor<OnDisplaysChangedListener>()
+    private val runningTasks = mutableListOf<RunningTaskInfo>()
+    private val externalDisplayId = 100
+    private val freeformTask =
+        TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build()
+    private val fullscreenTask =
+        TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
+    private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
 
     @Before
     fun setUp() {
@@ -99,8 +106,8 @@
         shellInit = spy(ShellInit(testExecutor))
         whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
         whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
-        val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
-        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+            .thenReturn(defaultTDA)
         handler =
             DesktopDisplayEventHandler(
                 context,
@@ -111,7 +118,11 @@
                 mockWindowManager,
                 mockDesktopUserRepositories,
                 mockDesktopTasksController,
+                shellTaskOrganizer,
             )
+        whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
+        runningTasks.add(freeformTask)
+        runningTasks.add(fullscreenTask)
         shellInit.init()
         verify(displayController)
             .addDisplayWindowListener(onDisplaysChangedListenerCaptor.capture())
@@ -127,9 +138,7 @@
         extendedDisplayEnabled: Boolean,
         expectTransition: Boolean,
     ) {
-        val externalDisplayId = 100
-        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
-        tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode
+        defaultTDA.configuration.windowConfiguration.windowingMode = defaultWindowingMode
         whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
         val settingsSession =
             ExtendedDisplaySettingsSession(
@@ -138,23 +147,17 @@
             )
 
         settingsSession.use {
-            // The external display connected.
-            whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
-                .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
-            onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
-            tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-            // The external display disconnected.
-            whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
-                .thenReturn(intArrayOf(DEFAULT_DISPLAY))
-            onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
+            connectExternalDisplay()
+            defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+            disconnectExternalDisplay()
 
             if (expectTransition) {
                 val arg = argumentCaptor<WindowContainerTransaction>()
                 verify(transitions, times(2))
                     .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
-                assertThat(arg.firstValue.changes[tda.token.asBinder()]?.windowingMode)
+                assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
                     .isEqualTo(WINDOWING_MODE_FREEFORM)
-                assertThat(arg.secondValue.changes[tda.token.asBinder()]?.windowingMode)
+                assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
                     .isEqualTo(defaultWindowingMode)
             } else {
                 verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
@@ -227,6 +230,58 @@
         verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
     }
 
+    @Test
+    fun displayWindowingModeSwitch_existingTasksOnConnected() {
+        defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+        whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
+            WINDOWING_MODE_FULLSCREEN
+        }
+
+        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
+            connectExternalDisplay()
+
+            val arg = argumentCaptor<WindowContainerTransaction>()
+            verify(transitions, times(1))
+                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+            assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_UNDEFINED)
+            assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+        }
+    }
+
+    @Test
+    fun displayWindowingModeSwitch_existingTasksOnDisconnected() {
+        defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+        whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
+            WINDOWING_MODE_FULLSCREEN
+        }
+
+        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
+            disconnectExternalDisplay()
+
+            val arg = argumentCaptor<WindowContainerTransaction>()
+            verify(transitions, times(1))
+                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+            assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FREEFORM)
+            assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_UNDEFINED)
+        }
+    }
+
+    private fun connectExternalDisplay() {
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
+        onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
+    }
+
+    private fun disconnectExternalDisplay() {
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY))
+        onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
+    }
+
     private class ExtendedDisplaySettingsSession(
         private val contentResolver: ContentResolver,
         private val overrideValue: Int,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
index 47d133b..006c3ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
@@ -23,7 +23,6 @@
 import android.os.IBinder
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.Display.DEFAULT_DISPLAY
@@ -73,7 +72,6 @@
 @RunWith(AndroidTestingRunner::class)
 class DesktopImmersiveControllerTest : ShellTestCase() {
 
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
     @JvmField @Rule val animatorTestRule = AnimatorTestRule(this)
 
     @Mock private lateinit var mockTransitions: Transitions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index 372e47c..f48bc99 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -27,7 +27,6 @@
 import android.os.IBinder
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.SurfaceControl
@@ -42,7 +41,6 @@
 import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
-import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.window.flags.Flags
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -58,7 +56,6 @@
 import org.junit.Assert.assertNull
 import org.junit.Assert.assertTrue
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyInt
@@ -83,8 +80,6 @@
 @RunWith(AndroidTestingRunner::class)
 class DesktopMixedTransitionHandlerTest : ShellTestCase() {
 
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
     @Mock lateinit var transitions: Transitions
     @Mock lateinit var userRepositories: DesktopUserRepositories
     @Mock lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler
@@ -143,10 +138,7 @@
     }
 
     @Test
-    @DisableFlags(
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
-    )
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
     fun startRemoveTransition_callsFreeformTaskTransitionHandler() {
         val wct = WindowContainerTransaction()
         whenever(freeformTaskTransitionHandler.startRemoveTransition(wct)).thenReturn(mock())
@@ -157,10 +149,7 @@
     }
 
     @Test
-    @EnableFlags(
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
-    )
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
     fun startRemoveTransition_startsCloseTransition() {
         val wct = WindowContainerTransaction()
         whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler))
@@ -200,10 +189,7 @@
     }
 
     @Test
-    @EnableFlags(
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
-    )
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
     fun startAnimation_withClosingDesktopTask_callsCloseTaskHandler() {
         val wct = WindowContainerTransaction()
         val transition = mock<IBinder>()
@@ -231,10 +217,7 @@
     }
 
     @Test
-    @EnableFlags(
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX,
-    )
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
     fun startAnimation_withClosingLastDesktopTask_dispatchesTransition() {
         val wct = WindowContainerTransaction()
         val transition = mock<IBinder>()
@@ -266,19 +249,11 @@
                 any(),
                 eq(mixedHandler),
             )
-        verify(interactionJankMonitor)
-            .begin(
-                closingTaskLeash,
-                context,
-                mockHandler,
-                CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE,
-            )
     }
 
     @Test
     @DisableFlags(
         Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
         Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
     )
     fun startLaunchTransition_immersiveAndAppLaunchFlagsDisabled_doesNotUseMixedHandler() {
@@ -316,10 +291,7 @@
     }
 
     @Test
-    @EnableFlags(
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
-    )
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
     fun startLaunchTransition_desktopAppLaunchEnabled_usesMixedHandler() {
         val wct = WindowContainerTransaction()
         val task = createTask(WINDOWING_MODE_FREEFORM)
@@ -415,10 +387,7 @@
     }
 
     @Test
-    @EnableFlags(
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
-    )
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
     fun startAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
         val wct = WindowContainerTransaction()
         val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -444,10 +413,7 @@
     }
 
     @Test
-    @EnableFlags(
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
-    )
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
     fun startAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
         val wct = WindowContainerTransaction()
         val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -476,10 +442,7 @@
     }
 
     @Test
-    @EnableFlags(
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
-    )
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
     fun startAnimation_pendingTransition_noLaunchChange_returnsFalse() {
         val wct = WindowContainerTransaction()
         val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -569,10 +532,7 @@
     }
 
     @Test
-    @EnableFlags(
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
-    )
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
     fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
         val wct = WindowContainerTransaction()
         val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -600,10 +560,7 @@
     }
 
     @Test
-    @EnableFlags(
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
-        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
-    )
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
     fun addPendingAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
         val wct = WindowContainerTransaction()
         val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index bddc062..8a5acfa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -19,7 +19,6 @@
 import android.app.ActivityManager.RunningTaskInfo
 import android.graphics.Rect
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
 import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker
@@ -65,15 +64,13 @@
     val displayLayout = mock<DisplayLayout>()
 
     @JvmField
-    @Rule(order = 0)
+    @Rule()
     val extendedMockitoRule =
         ExtendedMockitoRule.Builder(this)
             .mockStatic(FrameworkStatsLog::class.java)
             .mockStatic(EventLogTags::class.java)
             .build()!!
 
-    @JvmField @Rule(order = 1) val setFlagsRule = SetFlagsRule()
-
     @Before
     fun setUp() {
         doReturn(displayLayout).whenever(displayController).getDisplayLayout(anyInt())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
index 016e040..470c110 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
@@ -24,7 +24,6 @@
 import android.hardware.input.InputManager.KeyGestureEventHandler
 import android.hardware.input.KeyGestureEvent
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.KeyEvent
@@ -64,7 +63,6 @@
 import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.anyInt
@@ -87,8 +85,6 @@
 @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
 class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
 
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
     private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
     private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
     private val focusTransitionObserver = mock<FocusTransitionObserver>()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 5736793..6a343c5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -17,9 +17,9 @@
 package com.android.wm.shell.desktopmode
 
 import android.graphics.Rect
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
-import android.platform.test.flag.junit.SetFlagsRule
 import android.util.ArraySet
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.Display.INVALID_DISPLAY
@@ -27,6 +27,7 @@
 import com.android.window.flags.Flags
 import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE
 import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP
+import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestShellExecutor
 import com.android.wm.shell.common.ShellExecutor
@@ -35,6 +36,7 @@
 import com.android.wm.shell.sysui.ShellInit
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.fail
+import kotlin.test.assertEquals
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,7 +48,6 @@
 import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
@@ -70,8 +71,6 @@
 @ExperimentalCoroutinesApi
 class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
 
-    @JvmField @Rule val setFlagsRule = SetFlagsRule(flags)
-
     private lateinit var repo: DesktopRepository
     private lateinit var shellInit: ShellInit
     private lateinit var datastoreScope: CoroutineScope
@@ -1079,13 +1078,37 @@
         repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 1, isVisible = true)
         repo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
 
-        val tasksBeforeRemoval = repo.removeDesk(displayId = DEFAULT_DISPLAY)
+        val tasksBeforeRemoval = repo.removeDesk(deskId = DEFAULT_DISPLAY)
 
         assertThat(tasksBeforeRemoval).containsExactly(1, 2, 3).inOrder()
         assertThat(repo.getActiveTasks(displayId = DEFAULT_DISPLAY)).isEmpty()
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun removeDesk_multipleDesks_active_removes() {
+        repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+        repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+        repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+
+        repo.removeDesk(deskId = 3)
+
+        assertThat(repo.getDeskIds(displayId = DEFAULT_DISPLAY)).doesNotContain(3)
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun removeDesk_multipleDesks_inactive_removes() {
+        repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+        repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+        repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+
+        repo.removeDesk(deskId = 2)
+
+        assertThat(repo.getDeskIds(displayId = DEFAULT_DISPLAY)).doesNotContain(2)
+    }
+
+    @Test
     fun getTaskInFullImmersiveState_byDisplay() {
         repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
         repo.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -1159,6 +1182,34 @@
         assertThat(repo.shouldDesktopBeActiveForPip(DEFAULT_DESKTOP_ID)).isFalse()
     }
 
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun addTask_deskDoesNotExists_createsDesk() {
+        repo.addTask(displayId = 999, taskId = 6, isVisible = true)
+
+        assertThat(repo.getActiveTaskIdsInDesk(999)).contains(6)
+    }
+
+    @Test
+    @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun getDisplayForDesk() {
+        repo.addDesk(SECOND_DISPLAY, SECOND_DISPLAY)
+
+        assertEquals(SECOND_DISPLAY, repo.getDisplayForDesk(deskId = SECOND_DISPLAY))
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun getDisplayForDesk_multipleDesks() {
+        repo.addDesk(DEFAULT_DISPLAY, deskId = 6)
+        repo.addDesk(DEFAULT_DISPLAY, deskId = 7)
+        repo.addDesk(SECOND_DISPLAY, deskId = 8)
+        repo.addDesk(SECOND_DISPLAY, deskId = 9)
+
+        assertEquals(DEFAULT_DISPLAY, repo.getDisplayForDesk(deskId = 7))
+        assertEquals(SECOND_DISPLAY, repo.getDisplayForDesk(deskId = 8))
+    }
+
     class TestListener : DesktopRepository.ActiveTasksListener {
         var activeChangesOnDefaultDisplay = 0
         var activeChangesOnSecondaryDisplay = 0
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index 12c7ff6..50590f0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -18,7 +18,6 @@
 
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
@@ -26,7 +25,6 @@
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyInt
@@ -44,8 +42,6 @@
 @RunWith(AndroidTestingRunner::class)
 class DesktopTaskChangeListenerTest : ShellTestCase() {
 
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
     private lateinit var desktopTaskChangeListener: DesktopTaskChangeListener
 
     private val desktopUserRepositories = mock<DesktopUserRepositories>()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 0c1fd73..8e7545c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -35,6 +35,7 @@
 import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
 import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
 import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+import android.content.pm.PackageManager
 import android.content.res.Configuration.ORIENTATION_LANDSCAPE
 import android.content.res.Configuration.ORIENTATION_PORTRAIT
 import android.content.res.Resources
@@ -49,7 +50,6 @@
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
-import android.platform.test.flag.junit.SetFlagsRule
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.DragEvent
 import android.view.Gravity
@@ -100,6 +100,7 @@
 import com.android.wm.shell.common.MultiInstanceHelper
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.common.UserProfileContexts
 import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
@@ -117,7 +118,9 @@
 import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
 import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
+import com.android.wm.shell.desktopmode.multidesks.DeskTransition
 import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
+import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
 import com.android.wm.shell.desktopmode.persistence.Desktop
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
 import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
@@ -128,6 +131,7 @@
 import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING
 import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
 import com.android.wm.shell.shared.split.SplitScreenConstants
@@ -163,7 +167,6 @@
 import org.junit.After
 import org.junit.Assume.assumeTrue
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -179,6 +182,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.any
 import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argThat
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.atLeastOnce
 import org.mockito.kotlin.capture
@@ -199,8 +203,6 @@
 @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
 class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() {
 
-    @JvmField @Rule val setFlagsRule = SetFlagsRule(flags)
-
     @Mock lateinit var testExecutor: ShellExecutor
     @Mock lateinit var shellCommandHandler: ShellCommandHandler
     @Mock lateinit var shellController: ShellController
@@ -251,6 +253,8 @@
     @Mock
     private lateinit var overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver
     @Mock private lateinit var desksOrganizer: DesksOrganizer
+    @Mock private lateinit var userProfileContexts: UserProfileContexts
+    @Mock private lateinit var desksTransitionsObserver: DesksTransitionObserver
 
     private lateinit var controller: DesktopTasksController
     private lateinit var shellInit: ShellInit
@@ -259,6 +263,7 @@
     private lateinit var desktopTasksLimiter: DesktopTasksLimiter
     private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
     private lateinit var testScope: CoroutineScope
+    private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
 
     private val shellExecutor = TestShellExecutor()
 
@@ -309,6 +314,7 @@
                 mContext,
                 mockHandler,
             )
+        desktopModeCompatPolicy = DesktopModeCompatPolicy(context)
 
         whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
         whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
@@ -345,6 +351,8 @@
             )
             .thenReturn(ExitResult.NoExit)
         whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
+        whenever(userProfileContexts[anyInt()]).thenReturn(context)
+        whenever(userProfileContexts.getOrCreate(anyInt())).thenReturn(context)
 
         controller = createController()
         controller.setSplitScreenController(splitScreenController)
@@ -402,6 +410,9 @@
             Optional.of(bubbleController),
             overviewToDesktopTransitionObserver,
             desksOrganizer,
+            desksTransitionsObserver,
+            userProfileContexts,
+            desktopModeCompatPolicy,
         )
 
     @After
@@ -530,6 +541,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
         markTaskHidden(task1)
@@ -718,6 +730,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
         markTaskVisible(task1)
@@ -756,7 +769,8 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-    fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() {
+    fun showDesktopApps_someAppsInvisible_desktopWallpaperEnabled_reordersOnlyFreeformTasks() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
         markTaskHidden(task1)
@@ -773,6 +787,24 @@
         wct.assertReorderAt(index = 2, task2)
     }
 
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_someAppsInvisible_desktopWallpaperEnabled_reordersAll() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskHidden(task1)
+        markTaskVisible(task2)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: wallpaper intent, task1, task2
+        wct.assertReorderAt(index = 0, wallpaperToken)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() {
@@ -788,7 +820,9 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-    fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() {
+    fun showDesktopApps_noActiveTasks_desktopWallpaperEnabled_addsDesktopWallpaper() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
+
         controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
 
         val wct =
@@ -797,6 +831,16 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_noActiveTasks_desktopWallpaperEnabled_reordersDesktopWallpaper() {
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        wct.assertReorderAt(index = 0, wallpaperToken)
+    }
+
+    @Test
     @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() {
         taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -820,6 +864,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
         val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
         val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
@@ -864,6 +909,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun showDesktopApps_desktopWallpaperEnabled_dontReorderMinimizedTask() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val homeTask = setUpHomeTask()
         val freeformTask = setUpFreeformTask()
         val minimizedTask = setUpFreeformTask()
@@ -1329,6 +1375,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val task = createTaskInfo(1)
         whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
         whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
@@ -1414,6 +1461,41 @@
             .isEqualTo(WINDOWING_MODE_FREEFORM)
     }
 
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun moveRunningTaskToDesktop_defaultHomePackageWithDisplay_doesNothing() {
+        val packageManager: PackageManager = org.mockito.kotlin.mock()
+        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
+        val task =
+            setUpFullscreenTask().apply {
+                baseActivity = homeActivities
+                isTopActivityNoDisplay = false
+            }
+        mContext.setMockPackageManager(packageManager)
+        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)
+
+        controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+        verifyEnterDesktopWCTNotExecuted()
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun moveRunningTaskToDesktop_defaultHomePackageWithoutDisplay_doesNothing() {
+        val packageManager: PackageManager = org.mockito.kotlin.mock()
+        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
+        val task =
+            setUpFullscreenTask().apply {
+                baseActivity = homeActivities
+                isTopActivityNoDisplay = false
+            }
+        mContext.setMockPackageManager(packageManager)
+        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)
+
+        controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+
+        val wct = getLatestEnterDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun moveBackgroundTaskToDesktop_remoteTransition_usesOneShotHandler() {
@@ -1476,6 +1558,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val freeformTask = setUpFreeformTask()
         val fullscreenTask = setUpFullscreenTask()
         markTaskHidden(freeformTask)
@@ -1578,6 +1661,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun moveRunningTaskToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
         val newTask = setUpFullscreenTask()
         val homeTask = setUpHomeTask()
@@ -1616,6 +1700,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun moveToFullscreen_tdaFullscreen_windowingModeUndefined_removesWallpaperActivity() {
+        whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+        val homeTask = setUpHomeTask()
         val task = setUpFreeformTask()
         assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
             .configuration
@@ -1629,9 +1715,33 @@
         verify(desktopModeEnterExitTransitionListener)
             .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
         assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_UNDEFINED)
-        assertThat(wct.hierarchyOps).hasSize(1)
+        assertThat(wct.hierarchyOps).hasSize(3)
         // Removes wallpaper activity when leaving desktop
         wct.assertReorderAt(index = 0, wallpaperToken, toTop = false)
+        // Moves home task behind the fullscreen task
+        wct.assertReorderAt(index = 1, homeTask.getToken(), toTop = true)
+        wct.assertReorderAt(index = 2, task.getToken(), toTop = true)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
+    fun moveToFullscreen_tdaFreeform_enforcedDesktop_doesNotReorderHome() {
+        whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+        val homeTask = setUpHomeTask()
+        val task = setUpFreeformTask()
+        assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+            .configuration
+            .windowConfiguration
+            .windowingMode = WINDOWING_MODE_FREEFORM
+
+        controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN)
+
+        val wct = getLatestExitDesktopWct()
+        verify(desktopModeEnterExitTransitionListener)
+            .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+        assertThat(wct.hierarchyOps).hasSize(1)
+        // Removes wallpaper activity when leaving desktop but doesn't reorder home or the task
+        wct.assertReorderAt(index = 0, wallpaperToken, toTop = false)
     }
 
     @Test
@@ -1650,6 +1760,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun moveToFullscreen_tdaFreeform_windowingModeFullscreen_removesWallpaperActivity() {
+        val homeTask = setUpHomeTask()
         val task = setUpFreeformTask()
 
         assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
@@ -1664,13 +1775,17 @@
         assertThat(taskChange.windowingMode).isEqualTo(WINDOWING_MODE_FULLSCREEN)
         verify(desktopModeEnterExitTransitionListener)
             .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
-        assertThat(wct.hierarchyOps).hasSize(1)
+        assertThat(wct.hierarchyOps).hasSize(3)
         // Removes wallpaper activity when leaving desktop
         wct.assertReorderAt(index = 0, wallpaperToken, toTop = false)
+        // Moves home task behind the fullscreen task
+        wct.assertReorderAt(index = 1, homeTask.getToken(), toTop = true)
+        wct.assertReorderAt(index = 2, task.getToken(), toTop = true)
     }
 
     @Test
     fun moveToFullscreen_multipleVisibleNonMinimizedTasks_doesNotRemoveWallpaperActivity() {
+        val homeTask = setUpHomeTask()
         val task1 = setUpFreeformTask()
         // Setup task2
         setUpFreeformTask()
@@ -1688,7 +1803,10 @@
         verify(desktopModeEnterExitTransitionListener)
             .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
         // Does not remove wallpaper activity, as desktop still has a visible desktop task
-        assertThat(wct.hierarchyOps).isEmpty()
+        assertThat(wct.hierarchyOps).hasSize(2)
+        // Moves home task behind the fullscreen task
+        wct.assertReorderAt(index = 0, homeTask.getToken(), toTop = true)
+        wct.assertReorderAt(index = 1, task1.getToken(), toTop = true)
     }
 
     @Test
@@ -2564,6 +2682,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
         val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
         tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
@@ -2695,6 +2814,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val freeformTask1 = setUpFreeformTask()
         val freeformTask2 = createFreeformTask()
 
@@ -2729,7 +2849,9 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val task = createFreeformTask()
+
         val result = controller.handleRequest(Binder(), createTransition(task))
 
         assertNotNull(result, "Should handle request")
@@ -2757,6 +2879,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
         // Second display task
         createFreeformTask(displayId = SECOND_DISPLAY)
@@ -3010,6 +3133,67 @@
             .isEqualTo(WINDOWING_MODE_FREEFORM)
     }
 
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun handleRequest_defaultHomePackageWithDisplay_returnSwitchToFullscreenWCT() {
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+
+        val packageManager: PackageManager = org.mockito.kotlin.mock()
+        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
+        val task =
+            setUpFullscreenTask().apply {
+                baseActivity = homeActivities
+                isTopActivityNoDisplay = false
+            }
+        mContext.setMockPackageManager(packageManager)
+        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)
+
+        val result = controller.handleRequest(Binder(), createTransition(task))
+        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun handleRequest_defaultHomePackageWithoutDisplay_returnSwitchToFreeformWCT() {
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+
+        val packageManager: PackageManager = org.mockito.kotlin.mock()
+        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
+        val task =
+            setUpFullscreenTask().apply {
+                baseActivity = homeActivities
+                isTopActivityNoDisplay = false
+            }
+        mContext.setMockPackageManager(packageManager)
+        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)
+
+        val result = controller.handleRequest(Binder(), createTransition(task))
+        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
+    fun handleRequest_systemUIActivityWithDisplay_returnSwitchToFullscreenWCT_enforcedDesktop() {
+        whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+        // Set task as systemUI package
+        val systemUIPackageName =
+            context.resources.getString(com.android.internal.R.string.config_systemUi)
+        val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
+        val task =
+            createFreeformTask().apply {
+                baseActivity = baseComponent
+                isTopActivityNoDisplay = false
+            }
+
+        assertThat(controller.isDesktopModeShowing(DEFAULT_DISPLAY)).isFalse()
+        val result = controller.handleRequest(Binder(), createTransition(task))
+        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+    }
+
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun handleRequest_backTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
@@ -3392,6 +3576,7 @@
 
     @Test
     fun moveFocusedTaskToFullscreen_multipleVisibleTasks_doesNotRemoveWallpaperActivity() {
+        val homeTask = setUpHomeTask()
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
         val task3 = setUpFreeformTask()
@@ -3406,7 +3591,10 @@
         assertThat(taskChange.windowingMode)
             .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
         // Does not remove wallpaper activity, as desktop still has visible desktop tasks
-        assertThat(wct.hierarchyOps).isEmpty()
+        assertThat(wct.hierarchyOps).hasSize(2)
+        // Moves home task behind the fullscreen task
+        wct.assertReorderAt(index = 0, homeTask.getToken(), toTop = true)
+        wct.assertReorderAt(index = 1, task2.getToken(), toTop = true)
     }
 
     @Test
@@ -3436,13 +3624,14 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-    fun removeDesktop_multipleTasks_removesAll() {
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun removeDesk_multipleTasks_removesAll() {
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
         val task3 = setUpFreeformTask()
         taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
 
-        controller.removeDesktop(displayId = DEFAULT_DISPLAY)
+        controller.removeDefaultDeskInDisplay(displayId = DEFAULT_DISPLAY)
 
         val wct = getLatestWct(TRANSIT_CLOSE)
         assertThat(wct.hierarchyOps).hasSize(3)
@@ -3453,14 +3642,15 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-    fun removeDesktop_multipleTasksWithBackgroundTask_removesAll() {
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun removeDesk_multipleTasksWithBackgroundTask_removesAll() {
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
         val task3 = setUpFreeformTask()
         taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
         whenever(shellTaskOrganizer.getRunningTaskInfo(task3.taskId)).thenReturn(null)
 
-        controller.removeDesktop(displayId = DEFAULT_DISPLAY)
+        controller.removeDefaultDeskInDisplay(displayId = DEFAULT_DISPLAY)
 
         val wct = getLatestWct(TRANSIT_CLOSE)
         assertThat(wct.hierarchyOps).hasSize(2)
@@ -3470,6 +3660,30 @@
     }
 
     @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun removeDesk_multipleDesks_addsPendingTransition() {
+        val transition = Binder()
+        whenever(transitions.startTransition(eq(TRANSIT_CLOSE), any(), anyOrNull()))
+            .thenReturn(transition)
+        taskRepository.addDesk(DEFAULT_DISPLAY, deskId = 2)
+
+        controller.removeDesk(deskId = 2)
+
+        verify(desksOrganizer).removeDesk(any(), eq(2))
+        verify(desksTransitionsObserver)
+            .addPendingTransition(
+                argThat {
+                    this is DeskTransition.RemoveDesk &&
+                        this.token == transition &&
+                        this.deskId == 2
+                }
+            )
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
     fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
         val spyController = spy(controller)
@@ -3678,7 +3892,6 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            position = Point(100, -100),
             inputCoordinate = PointF(200f, -200f),
             currentDragBounds = Rect(100, -100, 500, 1000),
             validDragArea = Rect(0, 50, 2000, 2000),
@@ -3717,7 +3930,6 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            position = Point(100, 200),
             inputCoordinate = PointF(200f, 300f),
             currentDragBounds = currentDragBounds,
             validDragArea = Rect(0, 50, 2000, 2000),
@@ -3758,7 +3970,6 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            position = Point(100, 200),
             inputCoordinate = PointF(200f, 300f),
             currentDragBounds,
             validDragArea = Rect(0, 50, 2000, 2000),
@@ -3800,7 +4011,6 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            position = Point(100, 50),
             inputCoordinate = PointF(200f, 300f),
             currentDragBounds = Rect(100, 50, 500, 1000),
             validDragArea = Rect(0, 50, 2000, 2000),
@@ -3839,7 +4049,6 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            position = Point(100, 50),
             inputCoordinate = PointF(200f, 300f),
             currentDragBounds,
             validDragArea = Rect(0, 50, 2000, 2000),
@@ -3896,7 +4105,6 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            position = Point(100, 50),
             inputCoordinate = PointF(200f, 300f),
             currentDragBounds = currentDragBounds,
             validDragArea = Rect(0, 50, 2000, 2000),
@@ -5147,7 +5355,8 @@
         whenever(mockDragEvent.dragSurface).thenReturn(dragSurface)
         whenever(mockDragEvent.x).thenReturn(inputCoordinate.x)
         whenever(mockDragEvent.y).thenReturn(inputCoordinate.y)
-        whenever(multiInstanceHelper.supportsMultiInstanceSplit(anyOrNull())).thenReturn(true)
+        whenever(multiInstanceHelper.supportsMultiInstanceSplit(anyOrNull(), anyInt()))
+            .thenReturn(true)
         whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
         doReturn(indicatorType)
             .whenever(spyController)
@@ -5161,6 +5370,7 @@
 
         spyController.onUnhandledDrag(
             mockPendingIntent,
+            context.userId,
             mockDragEvent,
             mockCallback as Consumer<Boolean>,
         )
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 554b09f..d33209d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -24,7 +24,6 @@
 import android.os.UserManager
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.SurfaceControl
@@ -66,7 +65,6 @@
 import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
@@ -88,8 +86,6 @@
 @ExperimentalCoroutinesApi
 class DesktopTasksLimiterTest : ShellTestCase() {
 
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
     @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
     @Mock lateinit var transitions: Transitions
     @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index ca1e3ed..c29edec 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -25,7 +25,6 @@
 import android.os.Binder
 import android.os.IBinder
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.WindowManager
 import android.view.WindowManager.TRANSIT_CLOSE
@@ -48,11 +47,13 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP
 import com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP
+import com.android.wm.shell.util.StubTransaction
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Before
@@ -77,9 +78,6 @@
  * Build/Install/Run: atest WMShellUnitTests:DesktopTasksTransitionObserverTest
  */
 class DesktopTasksTransitionObserverTest {
-
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
     @JvmField
     @Rule
     val extendedMockitoRule =
@@ -96,6 +94,7 @@
     private val backAnimationController = mock<BackAnimationController>()
     private val desktopWallpaperActivityTokenProvider =
         mock<DesktopWallpaperActivityTokenProvider>()
+    private val desksTransitionObserver = mock<DesksTransitionObserver>()
     private val wallpaperToken = MockToken().token()
 
     private lateinit var transitionObserver: DesktopTasksTransitionObserver
@@ -119,6 +118,7 @@
                 mixedHandler,
                 backAnimationController,
                 desktopWallpaperActivityTokenProvider,
+                desksTransitionObserver,
                 shellInit,
             )
     }
@@ -415,6 +415,21 @@
         verify(taskRepository).setTaskInPip(task.displayId, task.taskId, enterPip = false)
     }
 
+    @Test
+    fun onTransitionReady_forwardsToDesksTransitionObserver() {
+        val transition = Binder()
+        val info = TransitionInfo(TRANSIT_CLOSE, /* flags= */ 0)
+
+        transitionObserver.onTransitionReady(
+            transition = transition,
+            info = info,
+            StubTransaction(),
+            StubTransaction(),
+        )
+
+        verify(desksTransitionObserver).onTransitionReady(transition, info)
+    }
+
     private fun createBackNavigationTransition(
         task: RunningTaskInfo?,
         type: Int = TRANSIT_TO_BACK,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index 5f8991e..8b6cafb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -74,10 +74,14 @@
             .setLastActiveTime(100)
             .build()
 
+    /**
+     * Create a new System Modal task builder, i.e. a builder for a task with only transparent
+     * activities.
+     */
+    fun createSystemModalTaskBuilder(displayId: Int = DEFAULT_DISPLAY): TestRunningTaskInfoBuilder =
+        createFullscreenTaskBuilder(displayId).setActivityStackTransparent(true).setNumActivities(1)
+
     /** Create a new System Modal task, i.e. a task with only transparent activities. */
     fun createSystemModalTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo =
-        createFullscreenTaskBuilder(displayId)
-            .setActivityStackTransparent(true)
-            .setNumActivities(1)
-            .build()
+        createSystemModalTaskBuilder(displayId).build()
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
index b9e307fa5..83e4872 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
@@ -21,7 +21,6 @@
 import android.os.UserManager
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -44,7 +43,6 @@
 import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.spy
@@ -56,8 +54,6 @@
 @RunWith(AndroidTestingRunner::class)
 @ExperimentalCoroutinesApi
 class DesktopUserRepositoriesTest : ShellTestCase() {
-    @get:Rule val setFlagsRule = SetFlagsRule()
-
     private lateinit var userRepositories: DesktopUserRepositories
     private lateinit var shellInit: ShellInit
     private lateinit var datastoreScope: CoroutineScope
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index bf9cf00..33dc1aa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -284,6 +284,7 @@
             cancelToken,
             TransitionInfo(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, 0),
             mock<SurfaceControl.Transaction>(),
+            mock<SurfaceControl.Transaction>(),
             startToken,
             mock<Transitions.TransitionFinishCallback>(),
         )
@@ -385,21 +386,23 @@
 
     @Test
     fun mergeAnimation_otherTransition_doesNotMerge() {
-        val transaction = mock<SurfaceControl.Transaction>()
+        val mergedStartTransaction = mock<SurfaceControl.Transaction>()
+        val mergedFinishTransaction = mock<SurfaceControl.Transaction>()
         val finishCallback = mock<Transitions.TransitionFinishCallback>()
         val task = createTask()
 
         startDrag(defaultHandler, task)
         defaultHandler.mergeAnimation(
-            transition = mock(),
+            transition = mock<IBinder>(),
             info = createTransitionInfo(type = TRANSIT_OPEN, draggedTask = task),
-            t = transaction,
-            mergeTarget = mock(),
+            startT = mergedStartTransaction,
+            finishT = mergedFinishTransaction,
+            mergeTarget = mock<IBinder>(),
             finishCallback = finishCallback,
         )
 
         // Should NOT have any transaction changes
-        verifyZeroInteractions(transaction)
+        verifyZeroInteractions(mergedStartTransaction)
         // Should NOT merge animation
         verify(finishCallback, never()).onTransitionFinished(any())
     }
@@ -408,6 +411,7 @@
     fun mergeAnimation_endTransition_mergesAnimation() {
         val playingFinishTransaction = mock<SurfaceControl.Transaction>()
         val mergedStartTransaction = mock<SurfaceControl.Transaction>()
+        val mergedFinishTransaction = mock<SurfaceControl.Transaction>()
         val finishCallback = mock<Transitions.TransitionFinishCallback>()
         val task = createTask()
         val startTransition =
@@ -415,13 +419,14 @@
         defaultHandler.onTaskResizeAnimationListener = mock()
 
         defaultHandler.mergeAnimation(
-            transition = mock(),
+            transition = mock<IBinder>(),
             info =
                 createTransitionInfo(
                     type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
                     draggedTask = task,
                 ),
-            t = mergedStartTransaction,
+            startT = mergedStartTransaction,
+            finishT = mergedFinishTransaction,
             mergeTarget = startTransition,
             finishCallback = finishCallback,
         )
@@ -440,6 +445,7 @@
         whenever(dragAnimator.computeCurrentVelocity()).thenReturn(PointF())
         val playingFinishTransaction = mock<SurfaceControl.Transaction>()
         val mergedStartTransaction = mock<SurfaceControl.Transaction>()
+        val mergedFinishTransaction = mock<SurfaceControl.Transaction>()
         val finishCallback = mock<Transitions.TransitionFinishCallback>()
         val task = createTask()
         val startTransition =
@@ -447,13 +453,14 @@
         springHandler.onTaskResizeAnimationListener = mock()
 
         springHandler.mergeAnimation(
-            transition = mock(),
+            transition = mock<IBinder>(),
             info =
                 createTransitionInfo(
                     type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
                     draggedTask = task,
                 ),
-            t = mergedStartTransaction,
+            startT = mergedStartTransaction,
+            finishT = mergedFinishTransaction,
             mergeTarget = startTransition,
             finishCallback = finishCallback,
         )
@@ -564,7 +571,8 @@
                     type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
                     draggedTask = task,
                 ),
-            t = mock<SurfaceControl.Transaction>(),
+            startT = mock<SurfaceControl.Transaction>(),
+            finishT = mock<SurfaceControl.Transaction>(),
             mergeTarget = startTransition,
             finishCallback = mock<Transitions.TransitionFinishCallback>(),
         )
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index 1569f9d..dfb1b0c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.desktopmode.compatui
 
+import android.content.Intent
 import android.os.Binder
 import android.testing.AndroidTestingRunner
 import android.view.SurfaceControl
@@ -29,7 +30,10 @@
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTaskBuilder
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSystemModalTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSystemModalTaskBuilder
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.desktopmode.DesktopWallpaperActivity
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
 import com.android.wm.shell.transition.Transitions
@@ -60,12 +64,14 @@
     private val finishT = mock<SurfaceControl.Transaction>()
 
     private lateinit var transitionHandler: SystemModalsTransitionHandler
+    private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
 
     @Before
     fun setUp() {
         // Simulate having one Desktop task so that we see Desktop Mode as active
         whenever(desktopUserRepositories.current).thenReturn(desktopRepository)
         whenever(desktopRepository.getVisibleTaskCount(anyInt())).thenReturn(1)
+        desktopModeCompatPolicy = DesktopModeCompatPolicy(context)
         transitionHandler = createTransitionHandler()
     }
 
@@ -77,6 +83,7 @@
             shellInit,
             transitions,
             desktopUserRepositories,
+            desktopModeCompatPolicy,
         )
 
     @Test
@@ -116,6 +123,19 @@
     }
 
     @Test
+    fun startAnimation_launchingWallpaperTask_doesNotAnimate() {
+        val wallpaperTask =
+            createSystemModalTaskBuilder().setBaseIntent(createWallpaperIntent()).build()
+        val info =
+            TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_OPEN, wallpaperTask).build()
+
+        assertThat(transitionHandler.startAnimation(Binder(), info, startT, finishT) {}).isFalse()
+    }
+
+    private fun createWallpaperIntent() =
+        Intent().apply { setComponent(DesktopWallpaperActivity.wallpaperActivityComponent) }
+
+    @Test
     fun startAnimation_launchingFullscreenTask_doesNotAnimate() {
         val info =
             TransitionInfoBuilder(TRANSIT_OPEN)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
index 5475032..86e8142 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
@@ -19,7 +19,6 @@
 import android.os.SystemProperties
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.testing.TestableContext
 import androidx.test.filters.SmallTest
@@ -29,7 +28,7 @@
 import com.android.wm.shell.desktopmode.CaptionState
 import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository
 import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.APP_HANDLE_EDUCATION_DELAY_MILLIS
-import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.APP_HANDLE_EDUCATION_TIMEOUT_MILLIS
+import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.TOOLTIP_VISIBLE_DURATION_MILLIS
 import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
@@ -47,7 +46,6 @@
 import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.test.setMain
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -76,7 +74,6 @@
             .mockStatic(DesktopModeStatus::class.java)
             .mockStatic(SystemProperties::class.java)
             .build()!!
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
 
     private lateinit var educationController: AppHandleEducationController
     private lateinit var testableContext: TestableContext
@@ -113,10 +110,10 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    fun init_appHandleVisible_shouldCallShowEducationTooltip() =
+    fun init_appHandleVisible_shouldCallShowEducationTooltipAndMarkAsViewed() =
         testScope.runTest {
             // App handle is visible. Should show education tooltip.
-            setShouldShowAppHandleEducation(true)
+            setShouldShowDesktopModeEducation(true)
 
             // Simulate app handle visible.
             testCaptionStateFlow.value = createAppHandleState()
@@ -124,6 +121,113 @@
             waitForBufferDelay()
 
             verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+            verify(mockDataStoreRepository, times(1))
+                .updateAppHandleHintViewedTimestampMillis(eq(true))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_appHandleEducationVisible_afterDelayTooltipShouldBeDismissed() =
+        testScope.runTest {
+            // App handle is visible. Should show education tooltip.
+            setShouldShowDesktopModeEducation(true)
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHandleState()
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            // Wait until tooltip gets dismissed
+            waitForBufferDelay(TOOLTIP_VISIBLE_DURATION_MILLIS + 1000L)
+
+            verify(mockTooltipController, times(1)).hideEducationTooltip()
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_appHandleVisibleAndMenuExpanded_shouldCallShowEducationTooltipAndMarkAsViewed() =
+        testScope.runTest {
+            setShouldShowDesktopModeEducation(true)
+
+            // Simulate app handle visible and handle menu is expanded.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+            waitForBufferDelay()
+
+            verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+            verify(mockDataStoreRepository, times(1))
+                .updateEnterDesktopModeHintViewedTimestampMillis(eq(true))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_appHeaderVisible_shouldCallShowEducationTooltipAndMarkAsViewed() =
+        testScope.runTest {
+            setShouldShowDesktopModeEducation(true)
+
+            // Simulate app header visible.
+            testCaptionStateFlow.value = createAppHeaderState()
+            waitForBufferDelay()
+
+            verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+            verify(mockDataStoreRepository, times(1))
+                .updateExitDesktopModeHintViewedTimestampMillis(eq(true))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_noCaptionStateNotified_shouldHideAllTooltips() =
+        testScope.runTest {
+            setShouldShowDesktopModeEducation(true)
+
+            // Simulate no caption state notification
+            testCaptionStateFlow.value = CaptionState.NoCaption
+            waitForBufferDelay()
+
+            verify(mockTooltipController, times(1)).hideEducationTooltip()
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_appHandleHintViewed_shouldNotListenToNoCaptionNotification() =
+        testScope.runTest {
+            testDataStoreFlow.value =
+                createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
+            setShouldShowDesktopModeEducation(true)
+
+            // Simulate no caption state notification
+            testCaptionStateFlow.value = CaptionState.NoCaption
+            waitForBufferDelay()
+
+            verify(mockTooltipController, never()).hideEducationTooltip()
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_enterDesktopModeHintViewed_shouldNotListenToNoCaptionNotification() =
+        testScope.runTest {
+            testDataStoreFlow.value =
+                createWindowingEducationProto(enterDesktopModeHintViewedTimestampMillis = 123L)
+            setShouldShowDesktopModeEducation(true)
+
+            // Simulate no caption state notification
+            testCaptionStateFlow.value = CaptionState.NoCaption
+            waitForBufferDelay()
+
+            verify(mockTooltipController, never()).hideEducationTooltip()
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_exitDesktopModeHintViewed_shouldNotListenToNoCaptionNotification() =
+        testScope.runTest {
+            testDataStoreFlow.value =
+                createWindowingEducationProto(exitDesktopModeHintViewedTimestampMillis = 123L)
+            setShouldShowDesktopModeEducation(true)
+
+            // Simulate no caption state notification
+            testCaptionStateFlow.value = CaptionState.NoCaption
+            waitForBufferDelay()
+
+            verify(mockTooltipController, never()).hideEducationTooltip()
         }
 
     @Test
@@ -133,7 +237,7 @@
             // App handle visible but education aconfig flag disabled, should not show education
             // tooltip.
             whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(false)
-            setShouldShowAppHandleEducation(true)
+            setShouldShowDesktopModeEducation(true)
 
             // Simulate app handle visible.
             testCaptionStateFlow.value = createAppHandleState()
@@ -145,12 +249,11 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    fun init_shouldShowAppHandleEducationReturnsFalse_shouldNotCallShowEducationTooltip() =
+    fun init_shouldShowDesktopModeEducationReturnsFalse_shouldNotCallShowEducationTooltip() =
         testScope.runTest {
-            // App handle is visible but [shouldShowAppHandleEducation] api returns false, should
-            // not
-            // show education tooltip.
-            setShouldShowAppHandleEducation(false)
+            // App handle is visible but [shouldShowDesktopModeEducation] api returns false, should
+            // not show education tooltip.
+            setShouldShowDesktopModeEducation(false)
 
             // Simulate app handle visible.
             testCaptionStateFlow.value = createAppHandleState()
@@ -165,7 +268,7 @@
     fun init_appHandleNotVisible_shouldNotCallShowEducationTooltip() =
         testScope.runTest {
             // App handle is not visible, should not show education tooltip.
-            setShouldShowAppHandleEducation(true)
+            setShouldShowDesktopModeEducation(true)
 
             // Simulate app handle is not visible.
             testCaptionStateFlow.value = CaptionState.NoCaption
@@ -184,7 +287,7 @@
             // Mark app handle hint viewed.
             testDataStoreFlow.value =
                 createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
-            setShouldShowAppHandleEducation(true)
+            setShouldShowDesktopModeEducation(true)
 
             // Simulate app handle visible.
             testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
@@ -196,6 +299,44 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_enterDesktopModeHintViewedAlready_shouldNotCallShowEducationTooltip() =
+        testScope.runTest {
+            // App handle is visible but app handle hint has been viewed before,
+            // should not show education tooltip.
+            // Mark app handle hint viewed.
+            testDataStoreFlow.value =
+                createWindowingEducationProto(enterDesktopModeHintViewedTimestampMillis = 123L)
+            setShouldShowDesktopModeEducation(true)
+
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, never()).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun init_exitDesktopModeHintViewedAlready_shouldNotCallShowEducationTooltip() =
+        testScope.runTest {
+            // App handle is visible but app handle hint has been viewed before,
+            // should not show education tooltip.
+            // Mark app handle hint viewed.
+            testDataStoreFlow.value =
+                createWindowingEducationProto(exitDesktopModeHintViewedTimestampMillis = 123L)
+            setShouldShowDesktopModeEducation(true)
+
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHeaderState()
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
+
+            verify(mockTooltipController, never()).showEducationTooltip(any(), any())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
     fun overridePrerequisite_appHandleHintViewedAlready_shouldCallShowEducationTooltip() =
         testScope.runTest {
             // App handle is visible but app handle hint has been viewed before.
@@ -204,11 +345,9 @@
             // Mark app handle hint viewed.
             testDataStoreFlow.value =
                 createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
-            val systemPropertiesKey =
-                "persist.desktop_windowing_app_handle_education_override_conditions"
-            whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
+            whenever(SystemProperties.getBoolean(eq(FORCE_SHOW_EDUCATION_SYSPROP), anyBoolean()))
                 .thenReturn(true)
-            setShouldShowAppHandleEducation(true)
+            setShouldShowDesktopModeEducation(true)
 
             // Simulate app handle visible.
             testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
@@ -220,207 +359,10 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    fun init_appHandleExpanded_shouldMarkAppHandleHintUsed() =
-        testScope.runTest {
-            setShouldShowAppHandleEducation(false)
-
-            // Simulate app handle visible and expanded.
-            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-            // Wait for some time before verifying
-            waitForBufferDelay()
-
-            verify(mockDataStoreRepository, times(1))
-                .updateAppHandleHintUsedTimestampMillis(eq(true))
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    fun init_showFirstTooltip_shouldMarkAppHandleHintViewed() =
+    fun clickAppHandleHint_openHandleMenuCallbackInvoked() =
         testScope.runTest {
             // App handle is visible. Should show education tooltip.
-            setShouldShowAppHandleEducation(true)
-
-            // Simulate app handle visible.
-            testCaptionStateFlow.value = createAppHandleState()
-            // Wait for first tooltip to showup.
-            waitForBufferDelay()
-
-            verify(mockDataStoreRepository, times(1))
-                .updateAppHandleHintViewedTimestampMillis(eq(true))
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    @Ignore("b/371527084: revisit testcase after refactoring original logic")
-    fun showWindowingImageButtonTooltip_appHandleExpanded_shouldCallShowEducationTooltipTwice() =
-        testScope.runTest {
-            // After first tooltip is dismissed, app handle is expanded. Should show second
-            // education
-            // tooltip.
-            showAndDismissFirstTooltip()
-
-            // Simulate app handle expanded.
-            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-            // Wait for next tooltip to showup.
-            waitForBufferDelay()
-
-            // [showEducationTooltip] should be called twice, once for each tooltip.
-            verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    @Ignore("b/371527084: revisit testcase after refactoring original logic")
-    fun showWindowingImageButtonTooltip_appHandleExpandedAfterTimeout_shouldCallShowEducationTooltipOnce() =
-        testScope.runTest {
-            // After first tooltip is dismissed, app handle is expanded after timeout. Should not
-            // show
-            // second education tooltip.
-            showAndDismissFirstTooltip()
-
-            // Wait for timeout to occur, after this timeout we should not listen for further
-            // triggers
-            // anymore.
-            advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
-            runCurrent()
-            // Simulate app handle expanded.
-            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-            // Wait for next tooltip to showup.
-            waitForBufferDelay()
-
-            // [showEducationTooltip] should be called once, just for the first tooltip.
-            verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    @Ignore("b/371527084: revisit testcase after refactoring original logic")
-    fun showWindowingImageButtonTooltip_appHandleExpandedTwice_shouldCallShowEducationTooltipTwice() =
-        testScope.runTest {
-            // After first tooltip is dismissed, app handle is expanded twice. Should show second
-            // education tooltip only once.
-            showAndDismissFirstTooltip()
-
-            // Simulate app handle expanded.
-            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-            // Wait for next tooltip to showup.
-            waitForBufferDelay()
-            // Simulate app handle being expanded twice.
-            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-            waitForBufferDelay()
-
-            // [showEducationTooltip] should not be called thrice, even if app handle was expanded
-            // twice. Should be called twice, once for each tooltip.
-            verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    @Ignore("b/371527084: revisit testcase after refactoring original logic")
-    fun showWindowingImageButtonTooltip_appHandleNotExpanded_shouldCallShowEducationTooltipOnce() =
-        testScope.runTest {
-            // After first tooltip is dismissed, app handle is not expanded. Should not show second
-            // education tooltip.
-            showAndDismissFirstTooltip()
-
-            // Simulate app handle visible but not expanded.
-            testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
-            // Wait for next tooltip to showup.
-            waitForBufferDelay()
-
-            // [showEducationTooltip] should be called once, just for the first tooltip.
-            verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    @Ignore("b/371527084: revisit testcase after refactoring original logic")
-    fun showExitWindowingButtonTooltip_appHeaderVisible_shouldCallShowEducationTooltipThrice() =
-        testScope.runTest {
-            // After first two tooltips are dismissed, app header is visible. Should show third
-            // education tooltip.
-            showAndDismissFirstTooltip()
-            showAndDismissSecondTooltip()
-
-            // Simulate app header visible.
-            testCaptionStateFlow.value = createAppHeaderState()
-            // Wait for next tooltip to showup.
-            waitForBufferDelay()
-
-            verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    @Ignore("b/371527084: revisit testcase after refactoring original logic")
-    fun showExitWindowingButtonTooltip_appHeaderVisibleAfterTimeout_shouldCallShowEducationTooltipTwice() =
-        testScope.runTest {
-            // After first two tooltips are dismissed, app header is visible after timeout. Should
-            // not
-            // show third education tooltip.
-            showAndDismissFirstTooltip()
-            showAndDismissSecondTooltip()
-
-            // Wait for timeout to occur, after this timeout we should not listen for further
-            // triggers
-            // anymore.
-            advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
-            runCurrent()
-            // Simulate app header visible.
-            testCaptionStateFlow.value = createAppHeaderState()
-            // Wait for next tooltip to showup.
-            waitForBufferDelay()
-
-            verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    @Ignore("b/371527084: revisit testcase after refactoring original logic")
-    fun showExitWindowingButtonTooltip_appHeaderVisibleTwice_shouldCallShowEducationTooltipThrice() =
-        testScope.runTest {
-            // After first two tooltips are dismissed, app header is visible twice. Should show
-            // third
-            // education tooltip only once.
-            showAndDismissFirstTooltip()
-            showAndDismissSecondTooltip()
-
-            // Simulate app header visible.
-            testCaptionStateFlow.value = createAppHeaderState()
-            // Wait for next tooltip to showup.
-            waitForBufferDelay()
-            testCaptionStateFlow.value = createAppHeaderState()
-            // Wait for next tooltip to showup.
-            waitForBufferDelay()
-
-            verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    @Ignore("b/371527084: revisit testcase after refactoring original logic")
-    fun showExitWindowingButtonTooltip_appHeaderExpanded_shouldCallShowEducationTooltipTwice() =
-        testScope.runTest {
-            // After first two tooltips are dismissed, app header is visible but expanded. Should
-            // not
-            // show third education tooltip.
-            showAndDismissFirstTooltip()
-            showAndDismissSecondTooltip()
-
-            // Simulate app header visible.
-            testCaptionStateFlow.value = createAppHeaderState(isHeaderMenuExpanded = true)
-            // Wait for next tooltip to showup.
-            waitForBufferDelay()
-
-            verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    fun setAppHandleEducationTooltipCallbacks_onAppHandleTooltipClicked_callbackInvoked() =
-        testScope.runTest {
-            // App handle is visible. Should show education tooltip.
-            setShouldShowAppHandleEducation(true)
+            setShouldShowDesktopModeEducation(true)
             val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
             val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
             educationController.setAppHandleEducationTooltipCallbacks(
@@ -441,76 +383,75 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
-    @Ignore("b/371527084: revisit testcase after refactoring original logic")
-    fun setAppHandleEducationTooltipCallbacks_onWindowingImageButtonTooltipClicked_callbackInvoked() =
+    fun clickEnterDesktopModeHint_toDesktopModeCallbackInvoked() =
         testScope.runTest {
-            // After first tooltip is dismissed, app handle is expanded. Should show second
-            // education
-            // tooltip.
-            showAndDismissFirstTooltip()
+            // App handle is visible. Should show education tooltip.
+            setShouldShowDesktopModeEducation(true)
             val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
             val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
             educationController.setAppHandleEducationTooltipCallbacks(
                 mockOpenHandleMenuCallback,
                 mockToDesktopModeCallback,
             )
-            // Simulate app handle expanded.
+            // Simulate app handle visible.
             testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-            // Wait for next tooltip to showup.
+            // Wait for first tooltip to showup.
             waitForBufferDelay()
 
             verify(mockTooltipController, atLeastOnce())
                 .showEducationTooltip(educationConfigCaptor.capture(), any())
             educationConfigCaptor.lastValue.onEducationClickAction.invoke()
 
-            verify(mockToDesktopModeCallback, times(1)).invoke(any(), any())
+            verify(mockToDesktopModeCallback, times(1))
+                .invoke(any(), eq(DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON))
         }
 
-    private suspend fun TestScope.showAndDismissFirstTooltip() {
-        setShouldShowAppHandleEducation(true)
-        // Simulate app handle visible.
-        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
-        // Wait for first tooltip to showup.
-        waitForBufferDelay()
-        // [shouldShowAppHandleEducation] should return false as education has been viewed
-        // before.
-        setShouldShowAppHandleEducation(false)
-        // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
-        captureAndInvokeOnDismissAction()
-    }
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    fun clickExitDesktopModeHint_openHandleMenuCallbackInvoked() =
+        testScope.runTest {
+            // App handle is visible. Should show education tooltip.
+            setShouldShowDesktopModeEducation(true)
+            val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
+            val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
+            educationController.setAppHandleEducationTooltipCallbacks(
+                mockOpenHandleMenuCallback,
+                mockToDesktopModeCallback,
+            )
+            // Simulate app handle visible.
+            testCaptionStateFlow.value = createAppHeaderState()
+            // Wait for first tooltip to showup.
+            waitForBufferDelay()
 
-    private fun TestScope.showAndDismissSecondTooltip() {
-        // Simulate app handle expanded.
-        testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
-        // Wait for next tooltip to showup.
-        waitForBufferDelay()
-        // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
-        captureAndInvokeOnDismissAction()
-    }
+            verify(mockTooltipController, atLeastOnce())
+                .showEducationTooltip(educationConfigCaptor.capture(), any())
+            educationConfigCaptor.lastValue.onEducationClickAction.invoke()
 
-    private fun captureAndInvokeOnDismissAction() {
-        verify(mockTooltipController, atLeastOnce())
-            .showEducationTooltip(educationConfigCaptor.capture(), any())
-        educationConfigCaptor.lastValue.onDismissAction.invoke()
-    }
+            verify(mockOpenHandleMenuCallback, times(1)).invoke(any())
+        }
 
-    private suspend fun setShouldShowAppHandleEducation(shouldShowAppHandleEducation: Boolean) =
-        whenever(mockEducationFilter.shouldShowAppHandleEducation(any()))
-            .thenReturn(shouldShowAppHandleEducation)
+    private suspend fun setShouldShowDesktopModeEducation(shouldShowDesktopModeEducation: Boolean) {
+        whenever(mockEducationFilter.shouldShowDesktopModeEducation(any<CaptionState.AppHandle>()))
+            .thenReturn(shouldShowDesktopModeEducation)
+        whenever(mockEducationFilter.shouldShowDesktopModeEducation(any<CaptionState.AppHeader>()))
+            .thenReturn(shouldShowDesktopModeEducation)
+    }
 
     /**
      * Class under test waits for some time before showing education, simulate advance time before
      * verifying or moving forward
      */
-    private fun TestScope.waitForBufferDelay() {
-        advanceTimeBy(APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS)
+    private fun TestScope.waitForBufferDelay(
+        delay: Long = APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS
+    ) {
+        advanceTimeBy(delay)
         runCurrent()
     }
 
     private companion object {
         val APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS: Long =
             APP_HANDLE_EDUCATION_DELAY_MILLIS + 1000L
-        val APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS: Long =
-            APP_HANDLE_EDUCATION_TIMEOUT_MILLIS + 1000L
+
+        val FORCE_SHOW_EDUCATION_SYSPROP = "persist.windowing_force_show_desktop_mode_education"
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
index 4db883d..31dfc78 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
@@ -123,6 +123,24 @@
         }
 
     @Test
+    fun updateEnterDesktopModeHintViewedTimestampMillis_updatesDatastoreProto() =
+        runTest(StandardTestDispatcher()) {
+            datastoreRepository.updateEnterDesktopModeHintViewedTimestampMillis(true)
+
+            val result = testDatastore.data.first().hasEnterDesktopModeHintViewedTimestampMillis()
+            assertThat(result).isEqualTo(true)
+        }
+
+    @Test
+    fun updateExitDesktopModeHintViewedTimestampMillis_updatesDatastoreProto() =
+        runTest(StandardTestDispatcher()) {
+            datastoreRepository.updateExitDesktopModeHintViewedTimestampMillis(true)
+
+            val result = testDatastore.data.first().hasExitDesktopModeHintViewedTimestampMillis()
+            assertThat(result).isEqualTo(true)
+        }
+
+    @Test
     fun updateAppHandleHintUsedTimestampMillis_updatesDatastoreProto() =
         runTest(StandardTestDispatcher()) {
             datastoreRepository.updateAppHandleHintUsedTimestampMillis(true)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
index 2fc36ef..2182262 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
@@ -89,9 +89,9 @@
     }
 
     @Test
-    fun shouldShowAppHandleEducation_isTriggerValid_returnsTrue() = runTest {
-        // setup() makes sure that all of the conditions satisfy and #shouldShowAppHandleEducation
-        // should return true
+    fun shouldShowDesktopModeEducation_isTriggerValid_returnsTrue() = runTest {
+        // setup() makes sure that all of the conditions satisfy and
+        // [shouldShowDesktopModeEducation] should return true
         val windowingEducationProto =
             createWindowingEducationProto(
                 appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
@@ -99,16 +99,15 @@
             )
         `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowDesktopModeEducation(createAppHandleState())
 
         assertThat(result).isTrue()
     }
 
     @Test
-    fun shouldShowAppHandleEducation_focusAppNotInAllowlist_returnsFalse() = runTest {
+    fun shouldShowDesktopModeEducation_focusAppNotInAllowlist_returnsFalse() = runTest {
         // Pass Youtube as current focus app, it is not in allowlist hence
-        // #shouldShowAppHandleEducation
-        // should return false
+        // [shouldShowDesktopModeEducation] should return false
         testableResources.addOverride(
             R.array.desktop_windowing_app_handle_education_allowlist_apps,
             arrayOf(GMAIL_PACKAGE_NAME),
@@ -122,16 +121,15 @@
             createAppHandleState(createTaskInfo(runningTaskPackageName = YOUTUBE_PACKAGE_NAME))
         `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-        val result = educationFilter.shouldShowAppHandleEducation(captionState)
+        val result = educationFilter.shouldShowDesktopModeEducation(captionState)
 
         assertThat(result).isFalse()
     }
 
     @Test
-    fun shouldShowAppHandleEducation_timeSinceSetupIsNotSufficient_returnsFalse() = runTest {
-        // Time required to have passed setup is > 100 years, hence #shouldShowAppHandleEducation
-        // should
-        // return false
+    fun shouldShowDesktopModeEducation_timeSinceSetupIsNotSufficient_returnsFalse() = runTest {
+        // Time required to have passed setup is > 100 years, hence [shouldShowDesktopModeEducation]
+        // should return false
         testableResources.addOverride(
             R.integer.desktop_windowing_education_required_time_since_setup_seconds,
             MAX_VALUE,
@@ -143,50 +141,15 @@
             )
         `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowDesktopModeEducation(createAppHandleState())
 
         assertThat(result).isFalse()
     }
 
     @Test
-    fun shouldShowAppHandleEducation_appHandleHintViewedBefore_returnsFalse() = runTest {
-        // App handle hint has been viewed before, hence #shouldShowAppHandleEducation should return
-        // false
-        val windowingEducationProto =
-            createWindowingEducationProto(
-                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
-                appHandleHintViewedTimestampMillis = 123L,
-                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
-            )
-        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
-        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
-        assertThat(result).isFalse()
-    }
-
-    @Test
-    fun shouldShowAppHandleEducation_appHandleHintUsedBefore_returnsFalse() = runTest {
-        // App handle hint has been used before, hence #shouldShowAppHandleEducation should return
-        // false
-        val windowingEducationProto =
-            createWindowingEducationProto(
-                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
-                appHandleHintUsedTimestampMillis = 123L,
-                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
-            )
-        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
-        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
-        assertThat(result).isFalse()
-    }
-
-    @Test
-    fun shouldShowAppHandleEducation_doesNotHaveMinAppUsage_returnsFalse() = runTest {
+    fun shouldShowDesktopModeEducation_doesNotHaveMinAppUsage_returnsFalse() = runTest {
         // Simulate that gmail app has been launched twice before, minimum app launch count is 3,
-        // hence
-        // #shouldShowAppHandleEducation should return false
+        // hence [shouldShowDesktopModeEducation] should return false
         testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
         val windowingEducationProto =
             createWindowingEducationProto(
@@ -195,13 +158,13 @@
             )
         `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowDesktopModeEducation(createAppHandleState())
 
         assertThat(result).isFalse()
     }
 
     @Test
-    fun shouldShowAppHandleEducation_appUsageStatsStale_queryAppUsageStats() = runTest {
+    fun shouldShowDesktopModeEducation_appUsageStatsStale_queryAppUsageStats() = runTest {
         // UsageStats caching interval is set to 0ms, that means caching should happen very
         // frequently
         testableResources.addOverride(
@@ -209,8 +172,7 @@
             0,
         )
         // The DataStore currently holds a proto object where Gmail's app launch count is recorded
-        // as 4.
-        // This value exceeds the minimum required count of 3.
+        // as 4. This value exceeds the minimum required count of 3.
         testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
         val windowingEducationProto =
             createWindowingEducationProto(
@@ -223,40 +185,20 @@
             .thenReturn(mapOf(GMAIL_PACKAGE_NAME to UsageStats().apply { mAppLaunchCount = 2 }))
         `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowDesktopModeEducation(createAppHandleState())
 
         // Result should be false as queried usage stats should be considered to determine the
-        // result
-        // instead of cached stats
+        // result instead of cached stats
         assertThat(result).isFalse()
     }
 
     @Test
-    fun shouldShowAppHandleEducation_appHandleMenuExpanded_returnsFalse() = runTest {
-        val windowingEducationProto =
-            createWindowingEducationProto(
-                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
-                appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
-            )
-        // Simulate app handle menu is expanded
-        val captionState = createAppHandleState(isHandleMenuExpanded = true)
-        `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
-        val result = educationFilter.shouldShowAppHandleEducation(captionState)
-
-        // We should not show app handle education if app menu is expanded
-        assertThat(result).isFalse()
-    }
-
-    @Test
-    fun shouldShowAppHandleEducation_overridePrerequisite_returnsTrue() = runTest {
+    fun shouldShowDesktopModeEducation_overridePrerequisite_returnsTrue() = runTest {
         // Simulate that gmail app has been launched twice before, minimum app launch count is 3,
-        // hence
-        // #shouldShowAppHandleEducation should return false. But as we are overriding prerequisite
-        // conditions, #shouldShowAppHandleEducation should return true.
+        // hence [shouldShowDesktopModeEducation] should return false. But as we are overriding
+        // prerequisite conditions, [shouldShowDesktopModeEducation] should return true.
         testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
-        val systemPropertiesKey =
-            "persist.desktop_windowing_app_handle_education_override_conditions"
+        val systemPropertiesKey = "persist.windowing_force_show_desktop_mode_education"
         whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
             .thenReturn(true)
         val windowingEducationProto =
@@ -266,7 +208,7 @@
             )
         `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
 
-        val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+        val result = educationFilter.shouldShowDesktopModeEducation(createAppHandleState())
 
         assertThat(result).isTrue()
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
new file mode 100644
index 0000000..bfbaa84
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.desktopmode.multidesks
+
+import android.os.Binder
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.WindowManager.TRANSIT_CLOSE
+import android.window.TransitionInfo
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.sysui.ShellInit
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+/**
+ * Tests for [DesksTransitionObserver].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesksTransitionObserverTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesksTransitionObserverTest : ShellTestCase() {
+
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+    private lateinit var desktopUserRepositories: DesktopUserRepositories
+    private lateinit var observer: DesksTransitionObserver
+
+    private val repository: DesktopRepository
+        get() = desktopUserRepositories.current
+
+    @Before
+    fun setUp() {
+        desktopUserRepositories =
+            DesktopUserRepositories(
+                context,
+                ShellInit(TestShellExecutor()),
+                /* shellController= */ mock(),
+                /* persistentRepository= */ mock(),
+                /* repositoryInitializer= */ mock(),
+                /* mainCoroutineScope= */ mock(),
+                /* userManager= */ mock(),
+            )
+        observer = DesksTransitionObserver(desktopUserRepositories)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun onTransitionReady_removeDesk_removesFromRepository() {
+        val transition = Binder()
+        val removeTransition =
+            DeskTransition.RemoveDesk(
+                transition,
+                displayId = DEFAULT_DISPLAY,
+                deskId = 5,
+                tasks = setOf(10, 11),
+                onDeskRemovedListener = null,
+            )
+        repository.addDesk(DEFAULT_DISPLAY, deskId = 5)
+
+        observer.addPendingTransition(removeTransition)
+        observer.onTransitionReady(
+            transition = transition,
+            info = TransitionInfo(TRANSIT_CLOSE, /* flags= */ 0),
+        )
+
+        assertThat(repository.getDeskIds(DEFAULT_DISPLAY)).doesNotContain(5)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun onTransitionReady_removeDesk_invokesOnRemoveListener() {
+        class FakeOnDeskRemovedListener : OnDeskRemovedListener {
+            var lastDeskRemoved: Int? = null
+
+            override fun onDeskRemoved(lastDisplayId: Int, deskId: Int) {
+                lastDeskRemoved = deskId
+            }
+        }
+        val transition = Binder()
+        val removeListener = FakeOnDeskRemovedListener()
+        val removeTransition =
+            DeskTransition.RemoveDesk(
+                transition,
+                displayId = DEFAULT_DISPLAY,
+                deskId = 5,
+                tasks = setOf(10, 11),
+                onDeskRemovedListener = removeListener,
+            )
+        repository.addDesk(DEFAULT_DISPLAY, deskId = 5)
+
+        observer.addPendingTransition(removeTransition)
+        observer.onTransitionReady(
+            transition = transition,
+            info = TransitionInfo(TRANSIT_CLOSE, /* flags= */ 0),
+        )
+
+        assertThat(removeListener.lastDeskRemoved).isEqualTo(5)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
index 9a8f264..dd9e6ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
@@ -19,7 +19,6 @@
 import android.os.UserManager
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.view.Display.DEFAULT_DISPLAY
 import androidx.test.filters.SmallTest
@@ -42,7 +41,6 @@
 import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.spy
@@ -54,8 +52,6 @@
 @ExperimentalCoroutinesApi
 class DesktopRepositoryInitializerTest : ShellTestCase() {
 
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
     private lateinit var repositoryInitializer: DesktopRepositoryInitializer
     private lateinit var shellInit: ShellInit
     private lateinit var datastoreScope: CoroutineScope
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 6c16b32..9509aaf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -35,7 +35,6 @@
 import android.app.ActivityManager;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.SurfaceControl;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -46,6 +45,7 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
 import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
 import com.android.wm.shell.desktopmode.DesktopRepository;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
@@ -55,7 +55,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -71,9 +70,6 @@
 @RunWith(AndroidJUnit4.class)
 public final class FreeformTaskListenerTests extends ShellTestCase {
 
-    @Rule
-    public final SetFlagsRule setFlagsRule = new SetFlagsRule();
-
     @Mock
     private ShellTaskOrganizer mTaskOrganizer;
     @Mock
@@ -89,6 +85,8 @@
     @Mock
     private DesktopTasksController mDesktopTasksController;
     @Mock
+    private DesktopModeLoggerTransitionObserver mDesktopModeLoggerTransitionObserver;
+    @Mock
     private LaunchAdjacentController mLaunchAdjacentController;
     @Mock
     private TaskChangeListener mTaskChangeListener;
@@ -114,6 +112,7 @@
                         mTaskOrganizer,
                         Optional.of(mDesktopUserRepositories),
                         Optional.of(mDesktopTasksController),
+                        mDesktopModeLoggerTransitionObserver,
                         mLaunchAdjacentController,
                         mWindowDecorViewModel,
                         Optional.of(mTaskChangeListener));
@@ -283,6 +282,19 @@
     }
 
     @Test
+    public void onTaskVanished_withDesktopModeLogger_forwards() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        mFreeformTaskListener.onTaskVanished(task);
+
+        verify(mDesktopModeLoggerTransitionObserver).onTaskVanished(task);
+    }
+
+
+    @Test
     public void onTaskInfoChanged_withDesktopController_forwards() {
         ActivityManager.RunningTaskInfo task =
                 new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 5aed461..bc91845 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -34,7 +34,6 @@
 import android.content.pm.PackageManager;
 import android.os.IBinder;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.SurfaceControl;
 import android.window.IWindowContainerToken;
 import android.window.TransitionInfo;
@@ -43,6 +42,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.window.flags.Flags;
+import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.desktopmode.DesktopImmersiveController;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.FocusTransitionObserver;
@@ -60,9 +60,8 @@
 
 /** Tests for {@link FreeformTaskTransitionObserver}. */
 @SmallTest
-public class FreeformTaskTransitionObserverTest {
+public class FreeformTaskTransitionObserverTest extends ShellTestCase {
 
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     @Mock private ShellInit mShellInit;
     @Mock private Transitions mTransitions;
     @Mock private DesktopImmersiveController mDesktopImmersiveController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 836f4c2..2668823 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -37,7 +37,6 @@
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.platform.test.annotations.DisableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Rational;
@@ -70,8 +69,6 @@
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentMatchers;
@@ -88,11 +85,6 @@
 @TestableLooper.RunWithLooper
 @DisableFlags(Flags.FLAG_ENABLE_PIP2)
 public class PipTaskOrganizerTest extends ShellTestCase {
-    @ClassRule
-    public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
-
     private PipTaskOrganizer mPipTaskOrganizer;
 
     @Mock private DisplayController mMockDisplayController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 5ef934c..13fce2a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -43,7 +43,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.platform.test.annotations.DisableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -77,8 +76,6 @@
 import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -95,9 +92,6 @@
 @TestableLooper.RunWithLooper
 @DisableFlags(Flags.FLAG_ENABLE_PIP2)
 public class PipControllerTest extends ShellTestCase {
-    @ClassRule public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
-    @Rule public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
-
     private PipController mPipController;
     private ShellInit mShellInit;
     private ShellController mShellController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index b11715b..273cb27 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -25,7 +25,6 @@
 
 import android.graphics.Rect;
 import android.platform.test.annotations.DisableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Size;
@@ -49,7 +48,6 @@
 import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -68,9 +66,6 @@
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class PipTouchHandlerTest extends ShellTestCase {
-    @Rule
-    public SetFlagsRule setFlagsRule = new SetFlagsRule();
-
     private static final int INSET = 10;
     private static final int PIP_LENGTH = 100;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
index 89cb729..3923a1e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.when;
 import static org.mockito.kotlin.MatchersKt.eq;
@@ -193,6 +194,12 @@
                 mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
                 mMockPipBoundsAlgorithm, mMockShellExecutor);
         mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+
+        // For this test case, any aspect ratio passed is considered within allowed range.
+        when(mMockPipBoundsAlgorithm
+                .isValidPictureInPictureAspectRatio(anyFloat()))
+                .thenReturn(true);
+
         Rational aspectRatio = new Rational(4, 3);
         when(mMockPipBoundsState.getAspectRatio()).thenReturn(aspectRatio.toFloat());
         String action1 = "action1";
@@ -227,6 +234,29 @@
     }
 
     @Test
+    public void onTaskInfoChanged_nonValidAspectRatio_doesNotCallbackAspectRatioChanged() {
+        mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+                mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+                mMockPipBoundsAlgorithm, mMockShellExecutor);
+        mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+
+        String action1 = "action1";
+        mPipTaskListener.onTaskInfoChanged(getTaskInfo(null, action1));
+        verify(mMockPipTransitionState, times(0))
+                .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+
+        // Define an invalid aspect ratio and try and update the params with it.
+        Rational aspectRatio = new Rational(100, 3);
+        when(mMockPipBoundsAlgorithm
+                .isValidPictureInPictureAspectRatio(eq(aspectRatio.floatValue())))
+                .thenReturn(false);
+
+        mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+        verify(mMockPipTransitionState, times(0))
+                .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+    }
+
+    @Test
     public void onPipTransitionStateChanged_scheduledBoundsChangeWithAspectRatioChange_schedule() {
         mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
                 mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 065fa21..5028479 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -63,7 +63,6 @@
 import android.os.Bundle;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.SurfaceControl;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -89,7 +88,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -129,9 +127,6 @@
     @Mock
     private DesktopRepository mDesktopRepository;
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     private ShellTaskOrganizer mShellTaskOrganizer;
     private RecentTasksController mRecentTasksController;
     private RecentTasksController mRecentTasksControllerReal;
@@ -211,6 +206,7 @@
 
     @Test
     public void testAddRemoveSplitNotifyChange() {
+        reset(mRecentTasksController);
         RecentTaskInfo t1 = makeTaskInfo(1);
         RecentTaskInfo t2 = makeTaskInfo(2);
         setRawList(t1, t2);
@@ -225,6 +221,7 @@
 
     @Test
     public void testAddSameSplitBoundsInfoSkipNotifyChange() {
+        reset(mRecentTasksController);
         RecentTaskInfo t1 = makeTaskInfo(1);
         RecentTaskInfo t2 = makeTaskInfo(2);
         setRawList(t1, t2);
@@ -535,6 +532,7 @@
 
     @Test
     public void testTaskWindowingModeChangedNotifiesChange() {
+        reset(mRecentTasksController);
         RecentTaskInfo t1 = makeTaskInfo(1);
         setRawList(t1);
 
@@ -551,7 +549,8 @@
                 WINDOWING_MODE_MULTI_WINDOW);
         mShellTaskOrganizer.onTaskInfoChanged(rt2MultiWIndow);
 
-        verify(mRecentTasksController).notifyRecentTasksChanged();
+        // One for onTaskAppeared and one for onTaskInfoChanged
+        verify(mRecentTasksController, times(2)).notifyRecentTasksChanged();
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
index ab43119..b50af74 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
@@ -47,7 +47,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
@@ -77,7 +76,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -115,9 +113,6 @@
 
     @Mock private DesktopRepository mDesktopRepository;
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     private ShellTaskOrganizer mShellTaskOrganizer;
     private RecentTasksController mRecentTasksController;
     private RecentTasksController mRecentTasksControllerReal;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
index 9919462..769407b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
@@ -26,7 +26,6 @@
 import android.os.IBinder
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.view.SurfaceControl
 import android.view.WindowManager
@@ -42,6 +41,7 @@
 import androidx.test.filters.SmallTest
 import com.android.window.flags.Flags
 import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestShellExecutor
 import com.android.wm.shell.TestSyncExecutor
 import com.android.wm.shell.common.ShellExecutor
@@ -53,7 +53,6 @@
 import com.google.common.truth.Truth.assertThat
 import dagger.Lazy
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -71,9 +70,7 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
-class TaskStackTransitionObserverTest {
-
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
+class TaskStackTransitionObserverTest : ShellTestCase() {
 
     @Mock private lateinit var shellInit: ShellInit
     @Mock private lateinit var shellTaskOrganizerLazy: Lazy<ShellTaskOrganizer>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt
index c19232b..4630649 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt
@@ -31,6 +31,7 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
@@ -56,6 +57,7 @@
         whenever(change.endAbsBounds).thenReturn(END_BOUNDS)
         whenever(transaction.setPosition(any(), anyFloat(), anyFloat())).thenReturn(transaction)
         whenever(transaction.setScale(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+        whenever(transaction.setFrameTimeline(anyLong())).thenReturn(transaction)
         whenever(
             transaction.setPosition(
                 any(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
new file mode 100644
index 0000000..a8a7be8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.shared.desktopmode
+
+import android.content.ComponentName
+import android.content.pm.PackageManager
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.wm.shell.compatui.CompatUIShellTestCase
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for [@link DesktopModeCompatPolicy].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopModeCompatPolicyTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class DesktopModeCompatPolicyTest : CompatUIShellTestCase() {
+    private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
+
+    @Before
+    fun setUp() {
+        desktopModeCompatPolicy = DesktopModeCompatPolicy(mContext)
+    }
+
+    @Test
+    fun testIsTopActivityExemptFromDesktopWindowing_onlyTransparentActivitiesInStack() {
+        assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+            createFreeformTask(/* displayId */ 0)
+                    .apply {
+                        isActivityStackTransparent = true
+                        isTopActivityNoDisplay = false
+                        numActivities = 1
+                    }))
+    }
+
+    @Test
+    fun testIsTopActivityExemptFromDesktopWindowing_noActivitiesInStack() {
+        assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+            createFreeformTask(/* displayId */ 0)
+                .apply {
+                    isActivityStackTransparent = true
+                    isTopActivityNoDisplay = false
+                    numActivities = 0
+                }))
+    }
+
+    @Test
+    fun testIsTopActivityExemptFromDesktopWindowing_nonTransparentActivitiesInStack() {
+        assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+            createFreeformTask(/* displayId */ 0)
+                .apply {
+                    isActivityStackTransparent = false
+                    isTopActivityNoDisplay = false
+                    numActivities = 1
+                }))
+    }
+
+    @Test
+    fun testIsTopActivityExemptFromDesktopWindowing_transparentActivityStack_notDisplayed() {
+        assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+            createFreeformTask(/* displayId */ 0)
+                .apply {
+                    isActivityStackTransparent = true
+                    isTopActivityNoDisplay = true
+                    numActivities = 1
+                }))
+    }
+
+    @Test
+    fun testIsTopActivityExemptFromDesktopWindowing_systemUiTask() {
+        val systemUIPackageName = context.resources.getString(R.string.config_systemUi)
+        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+            createFreeformTask(/* displayId */ 0)
+                    .apply {
+                        baseActivity = baseComponent
+                        isTopActivityNoDisplay = false
+                    }))
+    }
+
+    @Test
+    fun testIsTopActivityExemptFromDesktopWindowing_systemUiTask_notDisplayed() {
+        val systemUIPackageName = context.resources.getString(R.string.config_systemUi)
+        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+            createFreeformTask(/* displayId */ 0)
+                .apply {
+                    baseActivity = baseComponent
+                    isTopActivityNoDisplay = true
+                }))
+    }
+
+    @Test
+    fun testIsTopActivityExemptFromDesktopWindowing_defaultHomePackage() {
+        val packageManager: PackageManager = mock()
+        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
+        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)
+        mContext.setMockPackageManager(packageManager)
+        assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+            createFreeformTask(/* displayId */ 0)
+                .apply {
+                    baseActivity = homeActivities
+                    isTopActivityNoDisplay = false
+                }))
+    }
+
+    @Test
+    fun testIsTopActivityExemptFromDesktopWindowing_defaultHomePackage_notDisplayed() {
+        val packageManager: PackageManager = mock()
+        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
+        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)
+        mContext.setMockPackageManager(packageManager)
+        assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+            createFreeformTask(/* displayId */ 0)
+                .apply {
+                    baseActivity = homeActivities
+                    isTopActivityNoDisplay = true
+                }))
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
new file mode 100644
index 0000000..4dac99b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2025 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.shared.desktopmode
+
+import android.content.Context
+import android.content.res.Resources
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import android.provider.Settings
+import android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES
+import android.window.DesktopModeFlags
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@Presubmit
+@EnableFlags(Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+class DesktopModeStatusTest : ShellTestCase() {
+    @get:Rule
+    val mSetFlagsRule = SetFlagsRule()
+
+    private val mockContext = mock<Context>()
+    private val mockResources = mock<Resources>()
+
+    @Before
+    fun setUp() {
+        doReturn(mockResources).whenever(mockContext).getResources()
+        doReturn(false).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+        doReturn(false).whenever(mockResources).getBoolean(
+            eq(R.bool.config_isDesktopModeDevOptionSupported)
+        )
+        doReturn(context.contentResolver).whenever(mockContext).contentResolver
+        resetDesktopModeFlagsCache()
+        resetEnforceDeviceRestriction()
+        resetFlagOverride()
+    }
+
+    @After
+    fun tearDown() {
+        resetDesktopModeFlagsCache()
+        resetEnforceDeviceRestriction()
+        resetFlagOverride()
+    }
+
+    @DisableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+        Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
+    )
+    @Test
+    fun canEnterDesktopMode_DWFlagDisabled_configsOff_returnsFalse() {
+        assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
+    }
+
+    @DisableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+        Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
+    )
+    @Test
+    fun canEnterDesktopMode_DWFlagDisabled_configsOn_disableDeviceRestrictions_returnsFalse() {
+        doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+        doReturn(true).whenever(mockResources).getBoolean(
+            eq(R.bool.config_isDesktopModeDevOptionSupported)
+        )
+        disableEnforceDeviceRestriction()
+
+        assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
+    }
+
+    @DisableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+        Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
+    )
+    @Test
+    fun canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_returnsFalse() {
+        doReturn(true).whenever(mockResources).getBoolean(
+            eq(R.bool.config_isDesktopModeDevOptionSupported)
+        )
+
+        assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
+    }
+
+    @DisableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+        Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
+    )
+    @Test
+    fun canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_flagOverrideOn_returnsTrue() {
+        doReturn(true).whenever(mockResources).getBoolean(
+            eq(R.bool.config_isDesktopModeDevOptionSupported)
+        )
+        setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON)
+
+        assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    fun canEnterDesktopMode_DWFlagEnabled_configsOff_returnsFalse() {
+        assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    fun canEnterDesktopMode_DWFlagEnabled_configDesktopModeOff_returnsFalse() {
+        doReturn(true).whenever(mockResources).getBoolean(
+            eq(R.bool.config_isDesktopModeDevOptionSupported)
+        )
+
+        assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    fun canEnterDesktopMode_DWFlagEnabled_configDesktopModeOn_returnsTrue() {
+        doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+
+        assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    fun canEnterDesktopMode_DWFlagEnabled_configsOff_disableDeviceRestrictions_returnsTrue() {
+        disableEnforceDeviceRestriction()
+
+        assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    fun canEnterDesktopMode_DWFlagEnabled_configDevOptionOn_flagOverrideOn_returnsTrue() {
+        doReturn(true).whenever(mockResources).getBoolean(
+            eq(R.bool.config_isDesktopModeDevOptionSupported)
+        )
+        setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON)
+
+        assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
+    }
+
+    @Test
+    fun isDeviceEligibleForDesktopMode_configDEModeOn_returnsTrue() {
+        doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+
+        assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isTrue()
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @Test
+    fun isDeviceEligibleForDesktopMode_supportFlagOff_returnsFalse() {
+        assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse()
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @Test
+    fun isDeviceEligibleForDesktopMode_supportFlagOn_returnsFalse() {
+        assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse()
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @Test
+    fun isDeviceEligibleForDesktopMode_supportFlagOn_configDevOptModeOn_returnsTrue() {
+        doReturn(true).whenever(mockResources).getBoolean(
+            eq(R.bool.config_isDesktopModeDevOptionSupported)
+        )
+
+        assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isTrue()
+    }
+
+    private fun resetEnforceDeviceRestriction() {
+        setEnforceDeviceRestriction(true)
+    }
+
+    private fun disableEnforceDeviceRestriction() {
+        setEnforceDeviceRestriction(false)
+    }
+
+    private fun setEnforceDeviceRestriction(value: Boolean) {
+        val field = DesktopModeStatus::class.java.getDeclaredField("ENFORCE_DEVICE_RESTRICTIONS")
+        field.isAccessible = true
+        field.setBoolean(null, value)
+    }
+
+    private fun resetDesktopModeFlagsCache() {
+        val cachedToggleOverride =
+            DesktopModeFlags::class.java.getDeclaredField("sCachedToggleOverride")
+        cachedToggleOverride.isAccessible = true
+        cachedToggleOverride.set(null, null)
+    }
+
+    private fun resetFlagOverride() {
+        Settings.Global.putString(
+            context.contentResolver,
+            DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, null
+        )
+    }
+
+    private fun setFlagOverride(override: DesktopModeFlags.ToggleOverride) {
+        Settings.Global.putInt(
+            context.contentResolver,
+            DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, override.setting
+        )
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index bb9703f..7f6b06d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -213,7 +213,7 @@
 
     @Test
     public void startIntent_multiInstancesSupported_appendsMultipleTaskFag() {
-        doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
+        doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any(), anyInt());
         Intent startIntent = createStartIntent("startActivity");
         PendingIntent pendingIntent =
                 PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -252,13 +252,13 @@
 
         verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
                 isNull(), isNull(), eq(SPLIT_INDEX_0));
-        verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
+        verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any(), anyInt());
         verify(mStageCoordinator, never()).switchSplitPosition(any());
     }
 
     @Test
     public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() {
-        doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
+        doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any(), anyInt());
         doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any(), any());
         Intent startIntent = createStartIntent("startActivity");
         PendingIntent pendingIntent =
@@ -276,14 +276,14 @@
         mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
                 SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
                 SPLIT_INDEX_0);
-        verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
+        verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any(), anyInt());
         verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
                 isNull(), isNull(), eq(SPLIT_INDEX_0));
     }
 
     @Test
     public void startIntent_multiInstancesNotSupported_switchesPositionAfterSplitActivated() {
-        doReturn(false).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
+        doReturn(false).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any(), anyInt());
         Intent startIntent = createStartIntent("startActivity");
         PendingIntent pendingIntent =
                 PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 4211e46..b9d6a45 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -67,6 +67,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.MockToken;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -355,8 +356,13 @@
 
         // Make sure it cleans-up if recents doesn't restore
         WindowContainerTransaction commitWCT = new WindowContainerTransaction();
-        mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT,
-                mock(SurfaceControl.Transaction.class));
+        if (Flags.enableRecentsBookendTransition()) {
+            mStageCoordinator.onRecentsInSplitAnimationFinishing(false /* returnToApp */, commitWCT,
+                    mock(SurfaceControl.Transaction.class));
+        } else {
+            mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT,
+                    mock(SurfaceControl.Transaction.class));
+        }
         assertFalse(mStageCoordinator.isSplitScreenVisible());
     }
 
@@ -420,8 +426,13 @@
         // simulate the restoreWCT being applied:
         mMainStage.onTaskAppeared(mMainChild, mock(SurfaceControl.class));
         mSideStage.onTaskAppeared(mSideChild, mock(SurfaceControl.class));
-        mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT,
-                mock(SurfaceControl.Transaction.class));
+        if (Flags.enableRecentsBookendTransition()) {
+            mStageCoordinator.onRecentsInSplitAnimationFinishing(true /* returnToApp */, restoreWCT,
+                    mock(SurfaceControl.Transaction.class));
+        } else {
+            mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT,
+                    mock(SurfaceControl.Transaction.class));
+        }
         assertTrue(mStageCoordinator.isSplitScreenVisible());
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 6ac34d7..2d454a5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -51,7 +51,6 @@
 import android.graphics.Region;
 import android.os.Looper;
 import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.TestableLooper;
 import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
@@ -72,7 +71,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -96,9 +94,6 @@
         return FlagsParameterization.allCombinationsOf(Flags.FLAG_TASK_VIEW_REPOSITORY);
     }
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule;
-
     @Mock
     TaskView.Listener mViewListener;
     @Mock
@@ -127,9 +122,7 @@
     TaskViewTransitions mTaskViewTransitions;
     TaskViewTaskController mTaskViewTaskController;
 
-    public TaskViewTest(FlagsParameterization flags) {
-        mSetFlagsRule = new SetFlagsRule(flags);
-    }
+    public TaskViewTest(FlagsParameterization flags) {}
 
     @Before
     public void setUp() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index 326f11e..3a455ba 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -34,7 +34,6 @@
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.TestableLooper;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
@@ -50,7 +49,6 @@
 import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -74,9 +72,6 @@
                 Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP);
     }
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule;
-
     @Mock
     Transitions mTransitions;
     @Mock
@@ -95,9 +90,7 @@
     TaskViewRepository mTaskViewRepository;
     TaskViewTransitions mTaskViewTransitions;
 
-    public TaskViewTransitionsTest(FlagsParameterization flags) {
-        mSetFlagsRule = new SetFlagsRule(flags);
-    }
+    public TaskViewTransitionsTest(FlagsParameterization flags) {}
 
     @Before
     public void setUp() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
index e540322..82392e0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
@@ -292,6 +292,7 @@
                 new Binder(),
                 new TransitionInfoBuilder(TRANSIT_SLEEP, FLAG_SYNC).build(),
                 MockTransactionPool.create(),
+                MockTransactionPool.create(),
                 token,
                 mock(Transitions.TransitionFinishCallback.class));
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java
index 74c2f0e..96e4f49 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java
@@ -35,8 +35,6 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.os.RemoteException;
 import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionInfo.TransitionMode;
@@ -50,7 +48,6 @@
 import com.android.wm.shell.shared.FocusTransitionListener;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -67,8 +64,6 @@
 
     static final int SECONDARY_DISPLAY_ID = 1;
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
     private FocusTransitionListener mListener;
     private final TestShellExecutor mShellExecutor = new TestShellExecutor();
     private FocusTransitionObserver mFocusTransitionObserver;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 3e53ee5..6f28e656 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -39,8 +39,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionInfo.TransitionMode;
@@ -60,7 +58,6 @@
 import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -73,9 +70,6 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class HomeTransitionObserverTest extends ShellTestCase {
-
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
     private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class);
     private final TransactionPool mTransactionPool = mock(TransactionPool.class);
     private final Context mContext =
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 0a19be4..6f73db0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -74,7 +74,8 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
-import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.util.ArraySet;
 import android.util.Pair;
 import android.view.Surface;
@@ -117,7 +118,6 @@
 import com.android.wm.shell.util.StubTransaction;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
@@ -145,9 +145,6 @@
     private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler();
     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
 
-    @Rule
-    public final SetFlagsRule setFlagsRule = new SetFlagsRule();
-
     @Before
     public void setUp() {
         doAnswer(invocation -> new Binder())
@@ -553,7 +550,8 @@
     }
 
     @Test
-    public void testRegisteredRemoteTransitionTakeover() {
+    @DisableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
+    public void testRegisteredRemoteTransitionTakeover_flagDisabled() {
         Transitions transitions = createTestTransitions();
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
 
@@ -608,7 +606,6 @@
         mMainExecutor.flushAll();
 
         // Takeover shouldn't happen when the flag is disabled.
-        setFlagsRule.disableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED);
         IBinder transitToken = new Binder();
         transitions.requestStartTransition(transitToken,
                 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
@@ -621,12 +618,69 @@
         mDefaultHandler.finishAll();
         mMainExecutor.flushAll();
         verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
+    public void testRegisteredRemoteTransitionTakeover_flagEnabled() {
+        Transitions transitions = createTestTransitions();
+        transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+        IRemoteTransition testRemote = new RemoteTransitionStub() {
+            @Override
+            public void startAnimation(IBinder token, TransitionInfo info,
+                    SurfaceControl.Transaction t,
+                    IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+                final Transitions.TransitionHandler takeoverHandler =
+                        transitions.getHandlerForTakeover(token, info);
+
+                if (takeoverHandler == null) {
+                    finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
+                    return;
+                }
+
+                takeoverHandler.takeOverAnimation(token, info, new SurfaceControl.Transaction(),
+                        wct -> {
+                            try {
+                                finishCallback.onTransitionFinished(wct, null /* sct */);
+                            } catch (RemoteException e) {
+                                // Fail
+                            }
+                        }, new WindowAnimationState[info.getChanges().size()]);
+            }
+        };
+        final boolean[] takeoverRemoteCalled = new boolean[]{false};
+        IRemoteTransition testTakeoverRemote = new RemoteTransitionStub() {
+            @Override
+            public void startAnimation(IBinder token, TransitionInfo info,
+                    SurfaceControl.Transaction t,
+                    IRemoteTransitionFinishedCallback finishCallback) {}
+
+            @Override
+            public void takeOverAnimation(IBinder transition, TransitionInfo info,
+                    SurfaceControl.Transaction startTransaction,
+                    IRemoteTransitionFinishedCallback finishCallback, WindowAnimationState[] states)
+                    throws RemoteException {
+                takeoverRemoteCalled[0] = true;
+                finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
+            }
+        };
+
+        TransitionFilter filter = new TransitionFilter();
+        filter.mRequirements =
+                new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+        filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+
+        transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test"));
+        transitions.registerRemoteForTakeover(
+                filter, new RemoteTransition(testTakeoverRemote, "Test"));
+        mMainExecutor.flushAll();
 
         // Takeover should happen when the flag is enabled.
-        setFlagsRule.enableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED);
+        IBinder transitToken = new Binder();
         transitions.requestStartTransition(transitToken,
                 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
-        info = new TransitionInfoBuilder(TRANSIT_OPEN)
+        TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
                 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
         transitions.onTransitionReady(transitToken, info, new StubTransaction(),
                 new StubTransaction());
@@ -634,7 +688,7 @@
         assertTrue(takeoverRemoteCalled[0]);
         mDefaultHandler.finishAll();
         mMainExecutor.flushAll();
-        verify(mOrganizer, times(2)).finishTransition(eq(transitToken), any());
+        verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any());
     }
 
     @Test
@@ -1705,7 +1759,9 @@
 
         @Override
         public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-                @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+                @NonNull SurfaceControl.Transaction startT,
+                @NonNull SurfaceControl.Transaction finishT,
+                @NonNull IBinder mergeTarget,
                 @NonNull Transitions.TransitionFinishCallback finishCallback) {
             if (mFinishOnSync && info.getType() == TRANSIT_SLEEP) {
                 for (int i = 0; i < mFinishes.size(); ++i) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index 71af97e..aad18cb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -43,6 +43,7 @@
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
 
+import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestSyncExecutor;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.shared.TransactionPool;
@@ -61,7 +62,7 @@
 import java.util.List;
 import java.util.concurrent.Executor;
 
-public class UnfoldTransitionHandlerTest {
+public class UnfoldTransitionHandlerTest extends ShellTestCase {
 
     private UnfoldTransitionHandler mUnfoldTransitionHandler;
 
@@ -169,7 +170,8 @@
         // Send fold transition request
         TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class);
         mUnfoldTransitionHandler.mergeAnimation(new Binder(), createFoldTransitionInfo(),
-                mock(SurfaceControl.Transaction.class), mTransition, mergeFinishCallback);
+                mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
+                mTransition, mergeFinishCallback);
         mTestLooper.dispatchAll();
 
         // Verify that fold transition is merged into unfold and that unfold is finished
@@ -387,6 +389,7 @@
                 new Binder(),
                 new TransitionInfoBuilder(TRANSIT_CHANGE, TRANSIT_FLAG_KEYGUARD_GOING_AWAY).build(),
                 mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
                 mTransition,
                 mergeCallback);
         verify(finishCallback, never()).onTransitionFinished(any());
@@ -396,6 +399,7 @@
                 new Binder(),
                 new TransitionInfoBuilder(TRANSIT_CHANGE).build(),
                 mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
                 mTransition,
                 mergeCallback);
         verify(mergeCallback).onTransitionFinished(any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt
index b9d91e7..5468484 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt
@@ -81,7 +81,9 @@
     appHandleHintViewedTimestampMillis: Long? = null,
     appHandleHintUsedTimestampMillis: Long? = null,
     appUsageStats: Map<String, Int>? = null,
-    appUsageStatsLastUpdateTimestampMillis: Long? = null
+    appUsageStatsLastUpdateTimestampMillis: Long? = null,
+    enterDesktopModeHintViewedTimestampMillis: Long? = null,
+    exitDesktopModeHintViewedTimestampMillis: Long? = null,
 ): WindowingEducationProto =
     WindowingEducationProto.newBuilder()
         .apply {
@@ -91,6 +93,12 @@
           if (appHandleHintUsedTimestampMillis != null) {
             setAppHandleHintUsedTimestampMillis(appHandleHintUsedTimestampMillis)
           }
+          if (enterDesktopModeHintViewedTimestampMillis != null) {
+              setEnterDesktopModeHintViewedTimestampMillis(enterDesktopModeHintViewedTimestampMillis)
+          }
+          if (exitDesktopModeHintViewedTimestampMillis != null) {
+              setExitDesktopModeHintViewedTimestampMillis(exitDesktopModeHintViewedTimestampMillis)
+          }
           setAppHandleEducation(
               createAppHandleEducationProto(appUsageStats, appUsageStatsLastUpdateTimestampMillis))
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
index cf6c3a5e..257bbb5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
@@ -19,7 +19,6 @@
 import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.SurfaceControl
@@ -35,7 +34,6 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
@@ -51,10 +49,6 @@
 @RunWith(AndroidTestingRunner::class)
 class DesktopHeaderManageWindowsMenuTest : ShellTestCase() {
 
-    @JvmField
-    @Rule
-    val setFlagsRule: SetFlagsRule = SetFlagsRule()
-
     private lateinit var userRepositories: DesktopUserRepositories
     private lateinit var menu: DesktopHeaderManageWindowsMenu
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index baccbee..737780e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -28,6 +28,7 @@
 import android.content.Context
 import android.content.Intent
 import android.content.Intent.ACTION_MAIN
+import android.content.pm.PackageManager
 import android.graphics.Rect
 import android.graphics.Region
 import android.hardware.display.DisplayManager
@@ -96,7 +97,6 @@
 import org.mockito.quality.Strictness
 import java.util.function.Consumer
 
-
 /**
  * Tests of [DesktopModeWindowDecorViewModel]
  * Usage: atest WMShellUnitTests:DesktopModeWindowDecorViewModelTests
@@ -307,6 +307,23 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    fun testDecorationIsNotCreatedForDefaultHomePackage() {
+        val packageManager: PackageManager = org.mockito.kotlin.mock()
+        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN).apply {
+            baseActivity = homeActivities
+            isTopActivityNoDisplay = false
+        }
+        mContext.setMockPackageManager(packageManager)
+        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)
+
+        onTaskOpening(task)
+
+        assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_IMMERSIVE_HANDLE_HIDING)
     fun testInsetsStateChanged_notifiesAllDecorsInDisplay() {
         val task1 = createTask(windowingMode = WINDOWING_MODE_FREEFORM, displayId = 1)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 908bc99..7468c54 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -25,9 +25,6 @@
 import android.hardware.input.InputManager
 import android.os.Handler
 import android.os.UserHandle
-import android.platform.test.flag.junit.CheckFlagsRule
-import android.platform.test.flag.junit.DeviceFlagsValueProvider
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.TestableContext
 import android.util.SparseArray
 import android.view.Choreographer
@@ -69,6 +66,7 @@
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
@@ -83,7 +81,6 @@
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
 import org.junit.After
-import org.junit.Rule
 import org.mockito.Mockito
 import org.mockito.Mockito.anyInt
 import org.mockito.kotlin.any
@@ -104,14 +101,6 @@
  */
 @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
 open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
-    @JvmField
-    @Rule
-    val setFlagsRule = SetFlagsRule()
-
-    @JvmField
-    @Rule
-    val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
-
     private val mockDesktopModeWindowDecorFactory = mock<DesktopModeWindowDecoration.Factory>()
     protected val mockMainHandler = mock<Handler>()
     protected val mockMainChoreographer = mock<Choreographer>()
@@ -175,6 +164,7 @@
             DisplayChangeController.OnDisplayChangingListener
     internal lateinit var desktopModeOnKeyguardChangedListener: DesktopModeKeyguardChangeListener
     protected lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel
+    protected lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
 
     fun setUpCommon() {
         spyContext = spy(mContext)
@@ -188,6 +178,7 @@
         whenever(mockDisplayController.getDisplay(any())).thenReturn(display)
         whenever(mockDesktopUserRepositories.getProfile(anyInt()))
             .thenReturn(mockDesktopRepository)
+        desktopModeCompatPolicy = DesktopModeCompatPolicy(context)
         desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
             spyContext,
             testShellExecutor,
@@ -230,6 +221,7 @@
             mock<DesktopModeUiEventLogger>(),
             mock<WindowDecorTaskResourceLoader>(),
             mockRecentsTransitionHandler,
+            desktopModeCompatPolicy,
         )
         desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
         whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 9ea5fd6..18a780b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -20,7 +20,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
 import static android.view.WindowInsets.Type.captionBar;
@@ -69,7 +68,6 @@
 import android.os.SystemProperties;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableContext;
 import android.testing.TestableLooper;
@@ -129,7 +127,6 @@
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Ignore;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -171,8 +168,6 @@
     private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
     private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
 
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
     @Mock
     private DisplayController mMockDisplayController;
     @Mock
@@ -280,7 +275,7 @@
         mTestableContext = new TestableContext(mContext);
         mTestableContext.ensureTestableResources();
         mContext.setMockPackageManager(mMockPackageManager);
-        when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any()))
+        when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any(), anyInt()))
                 .thenReturn(false);
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("applicationLabel");
         final ActivityInfo activityInfo = createActivityInfo();
@@ -295,8 +290,10 @@
                 anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
                 any(), anyInt(), anyInt(), anyInt(), anyInt()))
                 .thenReturn(mMockHandleMenu);
-        when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
-        when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any()))
+        when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any(), anyInt()))
+                .thenReturn(false);
+        when(mMockAppHeaderViewHolderFactory
+                .create(any(), any(), any(), any(), any(), any(), any(), any(), any()))
                 .thenReturn(mMockAppHeaderViewHolder);
         when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
         when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index a20a89c..ab9dbc9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -23,7 +23,6 @@
 import android.os.IBinder
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.view.Display
 import android.window.WindowContainerToken
@@ -44,7 +43,6 @@
 import junit.framework.Assert.assertTrue
 import org.junit.After
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
@@ -85,10 +83,6 @@
     @Mock
     private lateinit var mockResources: Resources
 
-    @JvmField
-    @Rule
-    val setFlagsRule = SetFlagsRule()
-
     private lateinit var mockitoSession: StaticMockitoSession
 
     @Before
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
index 479f156..3389ec1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
@@ -30,7 +30,6 @@
 import android.graphics.Region;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.util.Size;
 
@@ -41,7 +40,6 @@
 
 import com.google.common.testing.EqualsTester;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -87,9 +85,6 @@
     private static final Point BOTTOM_INSET_POINT = new Point(TASK_SIZE.getWidth() / 2,
             TASK_SIZE.getHeight() - EDGE_RESIZE_HANDLE_INSET / 2);
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     /**
      * Check that both groups of objects satisfy equals/hashcode within each group, and that each
      * group is distinct from the next.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index f90988e..f984f6d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -26,7 +26,6 @@
 import android.graphics.Rect
 import android.graphics.drawable.BitmapDrawable
 import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.Display
@@ -60,7 +59,6 @@
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyInt
@@ -81,10 +79,6 @@
 @TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner::class)
 class HandleMenuTest : ShellTestCase() {
-    @JvmField
-    @Rule
-    val setFlagsRule: SetFlagsRule = SetFlagsRule()
-
     @Mock
     private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration
     @Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index d969346..3a8dcd6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -18,7 +18,6 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
 import static android.view.WindowInsets.Type.captionBar;
@@ -62,7 +61,6 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Handler;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.util.DisplayMetrics;
 import android.view.AttachedSurfaceControl;
@@ -93,7 +91,6 @@
 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -120,9 +117,6 @@
     private static final int SHADOW_RADIUS = 10;
     private static final int STATUS_BAR_INSET_SOURCE_ID = 0;
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
     private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
             new WindowDecoration.RelayoutResult<>();
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
index 431de89..c61e0eb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
@@ -86,6 +86,7 @@
         spyContext.setMockPackageManager(mockPackageManager)
         doReturn(spyContext).whenever(spyContext).createContextAsUser(any(), anyInt())
         doReturn(spyContext).whenever(mMockUserProfileContexts)[anyInt()]
+        doReturn(spyContext).whenever(mMockUserProfileContexts).getOrCreate(anyInt())
         loader =
             WindowDecorTaskResourceLoader(
                 shellInit = shellInit,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt
index 15f2c7b..3134b18 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt
@@ -154,12 +154,12 @@
   }
 
   @Test
-  fun showEducationTooltip_tooltipPointsLeft_verticallyPositionTooltip() {
+  fun showEducationTooltip_tooltipPointsHorizontally_verticallyPositionTooltip() {
     val initialTooltipX = 0
     val initialTooltipY = 0
     val tooltipViewConfig =
         createTooltipConfig(
-            arrowDirection = TooltipArrowDirection.LEFT,
+            arrowDirection = TooltipArrowDirection.HORIZONTAL,
             tooltipViewGlobalCoordinates = Point(initialTooltipX, initialTooltipY))
     val tooltipYArgumentCaptor = argumentCaptor<Int>()
     val tooltipHeightArgumentCaptor = argumentCaptor<Int>()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
index 997ece6e..2cabb9a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
@@ -23,6 +23,7 @@
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
@@ -30,6 +31,7 @@
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
 import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.transition.FocusTransitionObserver
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
 import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
@@ -67,6 +69,8 @@
     private val desktopModeWindowDecorationMock: DesktopModeWindowDecoration = mock()
     private val desktopTilingDecoration: DesktopTilingWindowDecoration = mock()
     private val taskResourceLoader: WindowDecorTaskResourceLoader = mock()
+    private val focusTransitionObserver: FocusTransitionObserver = mock()
+    private val mainExecutor: ShellExecutor = mock()
     private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
 
     @Before
@@ -86,6 +90,8 @@
                 userRepositories,
                 desktopModeEventLogger,
                 taskResourceLoader,
+                focusTransitionObserver,
+                mainExecutor
             )
         whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
     }
@@ -140,7 +146,7 @@
         desktopTilingDecorViewModel.moveTaskToFrontIfTiled(task1)
 
         verify(desktopTilingDecoration, times(1))
-            .moveTiledPairToFront(any(), isTaskFocused = eq(true))
+            .moveTiledPairToFront(any(), isFocusedOnDisplay = eq(true))
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
index 2f15c2e..399a51e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -33,6 +33,7 @@
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -42,6 +43,7 @@
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
 import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.transition.FocusTransitionObserver
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
 import com.android.wm.shell.windowdecor.DragResizeWindowGeometry
@@ -105,6 +107,8 @@
     private val mainDispatcher: MainCoroutineDispatcher = mock()
     private val bgScope: CoroutineScope = mock()
     private val taskResourceLoader: WindowDecorTaskResourceLoader = mock()
+    private val focusTransitionObserver: FocusTransitionObserver = mock()
+    private val mainExecutor: ShellExecutor = mock()
     private lateinit var tilingDecoration: DesktopTilingWindowDecoration
 
     private val split_divider_width = 10
@@ -129,6 +133,8 @@
                 returnToDragStartAnimator,
                 userRepositories,
                 desktopModeEventLogger,
+                focusTransitionObserver,
+                mainExecutor
             )
         whenever(context.createContextAsUser(any(), any())).thenReturn(context)
         whenever(userRepositories.current).thenReturn(desktopRepository)
@@ -242,7 +248,7 @@
             BOUNDS,
         )
 
-        assertThat(tilingDecoration.moveTiledPairToFront(task2)).isFalse()
+        assertThat(tilingDecoration.moveTiledPairToFront(task2.taskId, false)).isFalse()
         verify(transitions, never()).startTransition(any(), any(), any())
     }
 
@@ -272,7 +278,7 @@
             BOUNDS,
         )
 
-        assertThat(tilingDecoration.moveTiledPairToFront(task3)).isFalse()
+        assertThat(tilingDecoration.moveTiledPairToFront(task3.taskId, false)).isFalse()
         verify(transitions, never()).startTransition(any(), any(), any())
     }
 
@@ -304,7 +310,7 @@
         )
         task1.isFocused = true
 
-        assertThat(tilingDecoration.moveTiledPairToFront(task1, isTaskFocused = true)).isTrue()
+        assertThat(tilingDecoration.moveTiledPairToFront(task1.taskId, isFocusedOnDisplay = true)).isTrue()
         verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null))
     }
 
@@ -336,8 +342,8 @@
         task1.isFocused = true
         task3.isFocused = true
 
-        assertThat(tilingDecoration.moveTiledPairToFront(task3)).isFalse()
-        assertThat(tilingDecoration.moveTiledPairToFront(task1)).isTrue()
+        assertThat(tilingDecoration.moveTiledPairToFront(task3.taskId, true)).isFalse()
+        assertThat(tilingDecoration.moveTiledPairToFront(task1.taskId, true)).isTrue()
         verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null))
     }
 
@@ -367,8 +373,8 @@
             BOUNDS,
         )
 
-        assertThat(tilingDecoration.moveTiledPairToFront(task3, isTaskFocused = true)).isFalse()
-        assertThat(tilingDecoration.moveTiledPairToFront(task1, isTaskFocused = true)).isTrue()
+        assertThat(tilingDecoration.moveTiledPairToFront(task3.taskId, isFocusedOnDisplay = true)).isFalse()
+        assertThat(tilingDecoration.moveTiledPairToFront(task1.taskId, isFocusedOnDisplay = true)).isTrue()
         verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null))
     }
 
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index 139ccfd..7280b12 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -24,8 +24,8 @@
 
   public final class AppFunctionManager {
     ctor public AppFunctionManager(android.content.Context);
-    method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull com.android.extensions.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<com.android.extensions.appfunctions.ExecuteAppFunctionResponse,com.android.extensions.appfunctions.AppFunctionException>);
-    method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
+    method @RequiresPermission(android.Manifest.permission.EXECUTE_APP_FUNCTIONS) public void executeAppFunction(@NonNull com.android.extensions.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<com.android.extensions.appfunctions.ExecuteAppFunctionResponse,com.android.extensions.appfunctions.AppFunctionException>);
+    method @RequiresPermission(android.Manifest.permission.EXECUTE_APP_FUNCTIONS) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
     method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
     method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
     field public static final int APP_FUNCTION_STATE_DEFAULT = 0; // 0x0
diff --git a/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java
index 9eb66a3..1e31390 100644
--- a/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java
@@ -104,12 +104,7 @@
      * <p>See {@link android.app.appfunctions.AppFunctionManager#executeAppFunction} for the
      * documented behaviour of this method.
      */
-    @RequiresPermission(
-            anyOf = {
-                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
-                Manifest.permission.EXECUTE_APP_FUNCTIONS
-            },
-            conditional = true)
+    @RequiresPermission(Manifest.permission.EXECUTE_APP_FUNCTIONS)
     public void executeAppFunction(
             @NonNull ExecuteAppFunctionRequest sidecarRequest,
             @NonNull @CallbackExecutor Executor executor,
@@ -150,12 +145,7 @@
      * <p>See {@link android.app.appfunctions.AppFunctionManager#isAppFunctionEnabled} for the
      * documented behaviour of this method.
      */
-    @RequiresPermission(
-            anyOf = {
-                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
-                Manifest.permission.EXECUTE_APP_FUNCTIONS
-            },
-            conditional = true)
+    @RequiresPermission(Manifest.permission.EXECUTE_APP_FUNCTIONS)
     public void isAppFunctionEnabled(
             @NonNull String functionIdentifier,
             @NonNull String targetPackage,
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 677fd86..53d3b77 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -206,6 +206,9 @@
     visibility: [
         "//frameworks/base", // Framework
     ],
+    impl_library_visibility: [
+        "//frameworks/base/ravenwood",
+    ],
 
     srcs: [
         ":framework-graphics-srcs",
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 8bc66a0..f308ce9 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -18,6 +18,7 @@
 
 import static android.media.audio.Flags.FLAG_DOLBY_AC4_LEVEL4_ENCODING_API;
 import static android.media.audio.Flags.FLAG_IAMF_DEFINITIONS_API;
+import static android.media.audio.Flags.FLAG_SONY_360RA_MPEGH_3D_FORMAT;
 
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
@@ -718,8 +719,9 @@
      * Same as 9.1.4 with the addition of left and right top side channels */
     public static final int CHANNEL_OUT_9POINT1POINT6 = (CHANNEL_OUT_9POINT1POINT4
             | CHANNEL_OUT_TOP_SIDE_LEFT | CHANNEL_OUT_TOP_SIDE_RIGHT);
-    /** @hide */
-    public static final int CHANNEL_OUT_13POINT_360RA = (
+    /** Output channel mask for 13.0 */
+    @FlaggedApi(FLAG_SONY_360RA_MPEGH_3D_FORMAT)
+    public static final int CHANNEL_OUT_13POINT0 = (
             CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_FRONT_RIGHT |
             CHANNEL_OUT_SIDE_LEFT | CHANNEL_OUT_SIDE_RIGHT |
             CHANNEL_OUT_TOP_FRONT_LEFT | CHANNEL_OUT_TOP_FRONT_CENTER |
@@ -915,7 +917,7 @@
             case CHANNEL_OUT_9POINT1POINT6:
                 result.append("9.1.6");
                 break;
-            case CHANNEL_OUT_13POINT_360RA:
+            case CHANNEL_OUT_13POINT0:
                 result.append("360RA 13ch");
                 break;
             case CHANNEL_OUT_22POINT2:
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 52eae43..71013f7 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -21,6 +21,8 @@
 import static android.content.Context.DEVICE_ID_DEFAULT;
 import static android.media.audio.Flags.autoPublicVolumeApiHardening;
 import static android.media.audio.Flags.automaticBtDeviceType;
+import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
+import static android.media.audio.Flags.cacheGetStreamVolume;
 import static android.media.audio.Flags.FLAG_DEPRECATE_STREAM_BT_SCO;
 import static android.media.audio.Flags.FLAG_FOCUS_EXCLUSIVE_WITH_RECORDING;
 import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
@@ -58,7 +60,6 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.media.AudioAttributes.AttributeSystemUsage;
-import android.media.AudioDeviceInfo;
 import android.media.CallbackUtil.ListenerInfo;
 import android.media.audiopolicy.AudioPolicy;
 import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
@@ -75,6 +76,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IpcDataCache;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
@@ -1231,6 +1233,102 @@
     }
 
     /**
+     * API string for caching the min volume for each stream
+     * @hide
+     **/
+    public static final String VOLUME_MIN_CACHING_API = "getStreamMinVolume";
+    /**
+     * API string for caching the max volume for each stream
+     * @hide
+     **/
+    public static final String VOLUME_MAX_CACHING_API = "getStreamMaxVolume";
+    /**
+     * API string for caching the volume for each stream
+     * @hide
+     **/
+    public static final String VOLUME_CACHING_API = "getStreamVolume";
+    private static final int VOLUME_CACHING_SIZE = 16;
+
+    private final IpcDataCache.QueryHandler<VolumeCacheQuery, Integer> mVolQuery =
+            new IpcDataCache.QueryHandler<>() {
+                @Override
+                public Integer apply(VolumeCacheQuery query) {
+                    final IAudioService service = getService();
+                    try {
+                        return switch (query.queryCommand) {
+                            case QUERY_VOL_MIN -> service.getStreamMinVolume(query.stream);
+                            case QUERY_VOL_MAX -> service.getStreamMaxVolume(query.stream);
+                            case QUERY_VOL -> service.getStreamVolume(query.stream);
+                            default -> {
+                                Log.w(TAG, "Not a valid volume cache query: " + query);
+                                yield null;
+                            }
+                        };
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+
+            };
+
+    private final IpcDataCache<VolumeCacheQuery, Integer> mVolMinCache =
+            new IpcDataCache<>(VOLUME_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
+                    VOLUME_MIN_CACHING_API, VOLUME_MIN_CACHING_API, mVolQuery);
+
+    private final IpcDataCache<VolumeCacheQuery, Integer> mVolMaxCache =
+            new IpcDataCache<>(VOLUME_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
+                    VOLUME_MAX_CACHING_API, VOLUME_MAX_CACHING_API, mVolQuery);
+
+    private final IpcDataCache<VolumeCacheQuery, Integer> mVolCache =
+            new IpcDataCache<>(VOLUME_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
+                    VOLUME_CACHING_API, VOLUME_CACHING_API, mVolQuery);
+
+    /**
+     * Used to invalidate the cache for the given API
+     * @hide
+     **/
+    public static void clearVolumeCache(String api) {
+        if (cacheGetStreamMinMaxVolume() && (VOLUME_MAX_CACHING_API.equals(api)
+                || VOLUME_MIN_CACHING_API.equals(api))) {
+            IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, api);
+        } else if (cacheGetStreamVolume() && VOLUME_CACHING_API.equals(api)) {
+            IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, api);
+        } else {
+            Log.w(TAG, "invalid clearVolumeCache for api " + api);
+        }
+    }
+
+    private static final int QUERY_VOL_MIN = 1;
+    private static final int QUERY_VOL_MAX = 2;
+    private static final int QUERY_VOL = 3;
+    /** @hide */
+    @IntDef(prefix = "QUERY_VOL", value = {
+            QUERY_VOL_MIN,
+            QUERY_VOL_MAX,
+            QUERY_VOL}
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface QueryVolCommand {}
+
+    private record VolumeCacheQuery(int stream, @QueryVolCommand int queryCommand) {
+        private String queryVolCommandToString() {
+            return switch (queryCommand) {
+                case QUERY_VOL_MIN -> "getStreamMinVolume";
+                case QUERY_VOL_MAX -> "getStreamMaxVolume";
+                case QUERY_VOL -> "getStreamVolume";
+                default -> "invalid command";
+            };
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return TextUtils.formatSimple("VolumeCacheQuery(stream=%d, queryCommand=%s)", stream,
+                    queryVolCommandToString());
+        }
+    }
+
+    /**
      * Returns the maximum volume index for a particular stream.
      *
      * @param streamType The stream type whose maximum volume index is returned.
@@ -1238,6 +1336,9 @@
      * @see #getStreamVolume(int)
      */
     public int getStreamMaxVolume(int streamType) {
+        if (cacheGetStreamMinMaxVolume()) {
+            return mVolMaxCache.query(new VolumeCacheQuery(streamType, QUERY_VOL_MAX));
+        }
         final IAudioService service = getService();
         try {
             return service.getStreamMaxVolume(streamType);
@@ -1271,6 +1372,9 @@
      */
     @TestApi
     public int getStreamMinVolumeInt(int streamType) {
+        if (cacheGetStreamMinMaxVolume()) {
+            return mVolMinCache.query(new VolumeCacheQuery(streamType, QUERY_VOL_MIN));
+        }
         final IAudioService service = getService();
         try {
             return service.getStreamMinVolume(streamType);
@@ -1288,6 +1392,9 @@
      * @see #setStreamVolume(int, int, int)
      */
     public int getStreamVolume(int streamType) {
+        if (cacheGetStreamVolume()) {
+            return mVolCache.query(new VolumeCacheQuery(streamType, QUERY_VOL));
+        }
         final IAudioService service = getService();
         try {
             return service.getStreamVolume(streamType);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c9625c4..1a84371 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -20,6 +20,8 @@
 import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE;
 import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
 import static android.media.codec.Flags.FLAG_SUBSESSION_METRICS;
+import static android.media.tv.flags.Flags.applyPictureProfiles;
+import static android.media.tv.flags.Flags.mediaQualityFw;
 
 import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME;
 
@@ -37,6 +39,8 @@
 import android.graphics.SurfaceTexture;
 import android.hardware.HardwareBuffer;
 import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.quality.PictureProfile;
+import android.media.quality.PictureProfileHandle;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -44,6 +48,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.Trace;
 import android.view.Surface;
 
 import java.io.IOException;
@@ -3103,6 +3108,7 @@
             int index,
             int offset, int size, long presentationTimeUs, int flags)
         throws CryptoException {
+        Trace.traceBegin(Trace.TRACE_TAG_VIDEO, "MediaCodec::queueInputBuffer#java");
         if ((flags & BUFFER_FLAG_DECODE_ONLY) != 0
                 && (flags & BUFFER_FLAG_END_OF_STREAM) != 0) {
             throw new InvalidBufferFlagsException(EOS_AND_DECODE_ONLY_ERROR_MESSAGE);
@@ -3122,6 +3128,8 @@
         } catch (CryptoException | IllegalStateException e) {
             revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
             throw e;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIDEO);
         }
     }
 
@@ -3163,6 +3171,7 @@
     public final void queueInputBuffers(
             int index,
             @NonNull ArrayDeque<BufferInfo> bufferInfos) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIDEO, "MediaCodec::queueInputBuffers#java");
         synchronized(mBufferLock) {
             if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException("queueInputBuffers() "
@@ -3178,6 +3187,8 @@
         } catch (CryptoException | IllegalStateException | IllegalArgumentException e) {
             revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
             throw e;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIDEO);
         }
     }
 
@@ -3438,6 +3449,7 @@
             @NonNull CryptoInfo info,
             long presentationTimeUs,
             int flags) throws CryptoException {
+        Trace.traceBegin(Trace.TRACE_TAG_VIDEO, "MediaCodec::queueSecureInputBuffer#java");
         if ((flags & BUFFER_FLAG_DECODE_ONLY) != 0
                 && (flags & BUFFER_FLAG_END_OF_STREAM) != 0) {
             throw new InvalidBufferFlagsException(EOS_AND_DECODE_ONLY_ERROR_MESSAGE);
@@ -3457,6 +3469,8 @@
         } catch (CryptoException | IllegalStateException e) {
             revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
             throw e;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIDEO);
         }
     }
 
@@ -3487,6 +3501,7 @@
             int index,
             @NonNull ArrayDeque<BufferInfo> bufferInfos,
             @NonNull ArrayDeque<CryptoInfo> cryptoInfos) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIDEO, "MediaCodec::queueSecureInputBuffers#java");
         synchronized(mBufferLock) {
             if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException("queueSecureInputBuffers() "
@@ -3502,6 +3517,8 @@
         } catch (CryptoException | IllegalStateException | IllegalArgumentException e) {
             revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
             throw e;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIDEO);
         }
     }
 
@@ -3529,6 +3546,7 @@
      * @throws MediaCodec.CodecException upon codec error.
      */
     public final int dequeueInputBuffer(long timeoutUs) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIDEO, "MediaCodec::dequeueInputBuffer#java");
         synchronized (mBufferLock) {
             if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException("dequeueInputBuffer() "
@@ -3542,6 +3560,7 @@
                 validateInputByteBufferLocked(mCachedInputBuffers, res);
             }
         }
+        Trace.traceEnd(Trace.TRACE_TAG_VIDEO);
         return res;
     }
 
@@ -5370,6 +5389,9 @@
      * @param params The bundle of parameters to set.
      * @throws IllegalStateException if in the Released state.
      */
+
+    private static final String PARAMETER_KEY_PICTURE_PROFILE_HANDLE = "picture-profile-handle";
+
     public final void setParameters(@Nullable Bundle params) {
         if (params == null) {
             return;
@@ -5383,19 +5405,41 @@
             if (key.equals(MediaFormat.KEY_AUDIO_SESSION_ID)) {
                 int sessionId = 0;
                 try {
-                    sessionId = (Integer)params.get(key);
+                    sessionId = (Integer) params.get(key);
                 } catch (Exception e) {
                     throw new IllegalArgumentException("Wrong Session ID Parameter!");
                 }
                 keys[i] = "audio-hw-sync";
                 values[i] = AudioSystem.getAudioHwSyncForSession(sessionId);
+            } else if (applyPictureProfiles() && mediaQualityFw()
+                    && key.equals(MediaFormat.KEY_PICTURE_PROFILE_INSTANCE)) {
+                PictureProfile pictureProfile = null;
+                try {
+                    pictureProfile = (PictureProfile) params.get(key);
+                } catch (ClassCastException e) {
+                    throw new IllegalArgumentException(
+                            "Cannot cast the instance parameter to PictureProfile!");
+                } catch (Exception e) {
+                    android.util.Log.getStackTraceString(e);
+                    throw new IllegalArgumentException("Unexpected exception when casting the "
+                                                       + "instance parameter to PictureProfile!");
+                }
+                if (pictureProfile == null) {
+                    throw new IllegalArgumentException(
+                            "Picture profile instance parameter is null!");
+                }
+                PictureProfileHandle handle = pictureProfile.getHandle();
+                if (handle != PictureProfileHandle.NONE) {
+                    keys[i] = PARAMETER_KEY_PICTURE_PROFILE_HANDLE;
+                    values[i] = Long.valueOf(handle.getId());
+                }
             } else {
                 keys[i] = key;
                 Object value = params.get(key);
 
                 // Bundle's byte array is a byte[], JNI layer only takes ByteBuffer
                 if (value instanceof byte[]) {
-                    values[i] = ByteBuffer.wrap((byte[])value);
+                    values[i] = ByteBuffer.wrap((byte[]) value);
                 } else {
                     values[i] = value;
                 }
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index 8cc42e0..a600017 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -1,6 +1,7 @@
 # Bug component: 1344
-fgoldfain@google.com
+pshehane@google.com
 elaurent@google.com
+etalvala@google.com
 lajos@google.com
 jmtrivi@google.com
 
@@ -15,4 +16,5 @@
 # Haptics team also works on Ringtone
 per-file *Ringtone* = file:/services/core/java/com/android/server/vibrator/OWNERS
 
+per-file flags/media_better_together.aconfig = file:platform/frameworks/av:/media/janitors/better_together_OWNERS
 per-file flags/projection.aconfig = file:projection/OWNERS
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 3b8cf3f..87bb6ea 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -85,8 +85,7 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface TransferReason {}
 
-    @NonNull
-    final String mId;
+    @NonNull final String mOriginalId;
     @Nullable
     final CharSequence mName;
     @Nullable
@@ -120,7 +119,7 @@
     RoutingSessionInfo(@NonNull Builder builder) {
         Objects.requireNonNull(builder, "builder must not be null.");
 
-        mId = builder.mId;
+        mOriginalId = builder.mOriginalId;
         mName = builder.mName;
         mOwnerPackageName = builder.mOwnerPackageName;
         mClientPackageName = builder.mClientPackageName;
@@ -148,8 +147,8 @@
     }
 
     RoutingSessionInfo(@NonNull Parcel src) {
-        mId = src.readString();
-        Preconditions.checkArgument(!TextUtils.isEmpty(mId));
+        mOriginalId = src.readString();
+        Preconditions.checkArgument(!TextUtils.isEmpty(mOriginalId));
 
         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(src);
         mOwnerPackageName = src.readString();
@@ -221,9 +220,9 @@
     @NonNull
     public String getId() {
         if (!TextUtils.isEmpty(mProviderId)) {
-            return MediaRouter2Utils.toUniqueId(mProviderId, mId);
+            return MediaRouter2Utils.toUniqueId(mProviderId, mOriginalId);
         } else {
-            return mId;
+            return mOriginalId;
         }
     }
 
@@ -236,12 +235,16 @@
     }
 
     /**
-     * Gets the original id set by {@link Builder#Builder(String, String)}.
+     * Gets the original id as assigned by the {@link MediaRoute2ProviderService route provider}.
+     *
+     * <p>This may be different from {@link #getId()}, which may convert this original id into a
+     * unique one by adding information about the provider that created this session info.
+     *
      * @hide
      */
     @NonNull
     public String getOriginalId() {
-        return mId;
+        return mOriginalId;
     }
 
     /**
@@ -423,7 +426,7 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeString(mId);
+        dest.writeString(mOriginalId);
         dest.writeCharSequence(mName);
         dest.writeString(mOwnerPackageName);
         dest.writeString(mClientPackageName);
@@ -454,7 +457,7 @@
 
         String indent = prefix + "  ";
 
-        pw.println(indent + "mId=" + mId);
+        pw.println(indent + "mOriginalId=" + mOriginalId);
         pw.println(indent + "mName=" + mName);
         pw.println(indent + "mOwnerPackageName=" + mOwnerPackageName);
         pw.println(indent + "mClientPackageName=" + mClientPackageName);
@@ -485,7 +488,7 @@
         }
 
         RoutingSessionInfo other = (RoutingSessionInfo) obj;
-        return Objects.equals(mId, other.mId)
+        return Objects.equals(mOriginalId, other.mOriginalId)
                 && Objects.equals(mName, other.mName)
                 && Objects.equals(mOwnerPackageName, other.mOwnerPackageName)
                 && Objects.equals(mClientPackageName, other.mClientPackageName)
@@ -500,13 +503,13 @@
                 && (mTransferReason == other.mTransferReason)
                 && Objects.equals(mTransferInitiatorUserHandle, other.mTransferInitiatorUserHandle)
                 && Objects.equals(
-                mTransferInitiatorPackageName, other.mTransferInitiatorPackageName);
+                        mTransferInitiatorPackageName, other.mTransferInitiatorPackageName);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(
-                mId,
+                mOriginalId,
                 mName,
                 mOwnerPackageName,
                 mClientPackageName,
@@ -585,8 +588,7 @@
      * Builder class for {@link RoutingSessionInfo}.
      */
     public static final class Builder {
-        @NonNull
-        private final String mId;
+        @NonNull private final String mOriginalId;
         @Nullable
         private CharSequence mName;
         @Nullable
@@ -616,23 +618,22 @@
 
         /**
          * Constructor for builder to create {@link RoutingSessionInfo}.
-         * <p>
-         * In order to ensure ID uniqueness in {@link MediaRouter2} side, the value of
-         * {@link RoutingSessionInfo#getId()} can be different from what was set in
-         * {@link MediaRoute2ProviderService}.
-         * </p>
          *
-         * @param id ID of the session. Must not be empty.
-         * @param clientPackageName package name of the client app which uses this session.
-         *                          If is is unknown, then just use an empty string.
+         * <p>In order to ensure ID uniqueness in {@link MediaRouter2} side, the value of {@link
+         * RoutingSessionInfo#getId()} can be different from what was set in {@link
+         * MediaRoute2ProviderService}.
+         *
+         * @param originalId ID of the session. Must not be empty.
+         * @param clientPackageName package name of the client app which uses this session. If is is
+         *     unknown, then just use an empty string.
          * @see MediaRoute2Info#getId()
          */
-        public Builder(@NonNull String id, @NonNull String clientPackageName) {
-            if (TextUtils.isEmpty(id)) {
+        public Builder(@NonNull String originalId, @NonNull String clientPackageName) {
+            if (TextUtils.isEmpty(originalId)) {
                 throw new IllegalArgumentException("id must not be empty");
             }
 
-            mId = id;
+            mOriginalId = originalId;
             mClientPackageName =
                     Objects.requireNonNull(clientPackageName, "clientPackageName must not be null");
             mSelectedRoutes = new ArrayList<>();
@@ -650,7 +651,7 @@
         public Builder(@NonNull RoutingSessionInfo sessionInfo) {
             Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
 
-            mId = sessionInfo.mId;
+            mOriginalId = sessionInfo.mOriginalId;
             mName = sessionInfo.mName;
             mClientPackageName = sessionInfo.mClientPackageName;
             mProviderId = sessionInfo.mProviderId;
diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java
index 8521d1c..b831e4f 100644
--- a/media/java/android/media/audio/common/AidlConversion.java
+++ b/media/java/android/media/audio/common/AidlConversion.java
@@ -366,8 +366,8 @@
                             return AudioFormat.CHANNEL_OUT_9POINT1POINT4;
                         case AudioChannelLayout.LAYOUT_9POINT1POINT6:
                             return AudioFormat.CHANNEL_OUT_9POINT1POINT6;
-                        case AudioChannelLayout.LAYOUT_13POINT_360RA:
-                            return AudioFormat.CHANNEL_OUT_13POINT_360RA;
+                        case AudioChannelLayout.LAYOUT_13POINT0:
+                            return AudioFormat.CHANNEL_OUT_13POINT0;
                         case AudioChannelLayout.LAYOUT_22POINT2:
                             return AudioFormat.CHANNEL_OUT_22POINT2;
                         case AudioChannelLayout.LAYOUT_MONO_HAPTIC_A:
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 28f238e..0dd8d19 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -21,6 +21,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -80,7 +81,8 @@
 
     private final static String TAG = "AudioEffect-JAVA";
 
-    // effect type UUIDs are taken from hardware/libhardware/include/hardware/audio_effect.h
+    // effect type UUIDs are taken
+    //     from hardware/interfaces/audio/aidl/android/hardware/audio/effect/Descriptor.aidl
 
     /**
      * The following UUIDs define effect types corresponding to standard audio
@@ -148,6 +150,16 @@
               .fromString("7261676f-6d75-7369-6364-28e2fd3ac39e");
 
     /**
+     * @hide
+     * UUID for the Spatializer effect
+     */
+    @SuppressLint("UnflaggedApi") // Test API
+    @TestApi
+    @NonNull
+    public static final UUID EFFECT_TYPE_SPATIALIZER =
+            UUID.fromString("ccd4cf09-a79d-46c2-9aae-06a1698d6c8f");
+
+    /**
      * UUID for Haptic Generator.
      */
     // This is taken from system/media/audio/include/system/audio_effects/effect_hapticgenerator.h
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 94454cf..405d292 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -125,6 +125,13 @@
 }
 
 flag {
+    name: "enable_output_switcher_session_grouping"
+    namespace: "media_better_together"
+    description: "Enables selected items in Output Switcher to be grouped together."
+    bug: "388347018"
+}
+
+flag {
     name: "enable_prevention_of_keep_alive_route_providers"
     namespace: "media_solutions"
     description: "Enables mechanisms to prevent route providers from keeping malicious apps alive."
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index d1f6340..e4de3e4 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -341,6 +341,13 @@
         public static final String PARAMETER_FILM_MODE = "film_mode";
 
         /**
+         * Enable/disable black color auto stretch
+         *
+         * @hide
+         */
+        public static final String PARAMETER_BLACK_STRETCH = "black_stretch";
+
+        /**
          * Enable/disable blue color auto stretch
          *
          * <p>Type: BOOLEAN
@@ -385,6 +392,353 @@
         public static final String PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED =
                 "auto_super_resolution_enabled";
 
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_LEVEL_RANGE = "level_range";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_GAMUT_MAPPING = "gamut_mapping";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_PC_MODE = "pc_mode";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_LOW_LATENCY = "low_latency";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_VRR = "vrr";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_CVRR = "cvrr";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_HDMI_RGB_RANGE = "hdmi_rgb_range";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_SPACE = "color_space";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS =
+                "panel_init_max_lumince_nits";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID =
+                "panel_init_max_lumince_valid";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_GAMMA = "gamma";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TEMPERATURE_RED_GAIN =
+                "color_temperature_red_gain";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TEMPERATURE_GREEN_GAIN =
+                "color_temperature_green_gain";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TEMPERATURE_BLUE_GAIN =
+                "color_temperature_blue_gain";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TEMPERATURE_RED_OFFSET =
+                "color_temperature_red_offset";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET =
+                "color_temperature_green_offset";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET =
+                "color_temperature_blue_offset";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_ELEVEN_POINT_RED = "eleven_point_red";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_ELEVEN_POINT_GREEN = "eleven_point_green";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_ELEVEN_POINT_BLUE = "eleven_point_blue";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_LOW_BLUE_LIGHT = "low_blue_light";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_LD_MODE = "ld_mode";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_OSD_RED_GAIN = "osd_red_gain";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_OSD_GREEN_GAIN = "osd_green_gain";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_OSD_BLUE_GAIN = "osd_blue_gain";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_OSD_RED_OFFSET = "osd_red_offset";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_OSD_GREEN_OFFSET = "osd_green_offset";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_OSD_BLUE_OFFSET = "osd_blue_offset";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_OSD_HUE = "osd_hue";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_OSD_SATURATION = "osd_saturation";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_OSD_CONTRAST = "osd_contrast";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_SWITCH = "color_tuner_switch";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_HUE_RED = "color_tuner_hue_red";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_HUE_GREEN = "color_tuner_hue_green";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_HUE_BLUE = "color_tuner_hue_blue";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_HUE_CYAN = "color_tuner_hue_cyan";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_HUE_MAGENTA = "color_tuner_hue_magenta";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_HUE_YELLOW = "color_tuner_hue_yellow";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_HUE_FLESH = "color_tuner_hue_flesh";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_SATURATION_RED =
+                "color_tuner_saturation_red";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_SATURATION_GREEN =
+                "color_tuner_saturation_green";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_SATURATION_BLUE =
+                "color_tuner_saturation_blue";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_SATURATION_CYAN =
+                "color_tuner_saturation_cyan";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_SATURATION_MAGENTA =
+                "color_tuner_saturation_magenta";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_SATURATION_YELLOW =
+                "color_tuner_saturation_yellow";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_SATURATION_FLESH =
+                "color_tuner_saturation_flesh";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_LUMINANCE_RED =
+                "color_tuner_luminance_red";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_LUMINANCE_GREEN =
+                "color_tuner_luminance_green";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_LUMINANCE_BLUE =
+                "color_tuner_luminance_blue";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_LUMINANCE_CYAN =
+                "color_tuner_luminance_cyan";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA =
+                "color_tuner_luminance_magenta";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW =
+                "color_tuner_luminance_yellow";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_COLOR_TUNER_LUMINANCE_FLESH =
+                "color_tuner_luminance_flesh";
+
+        /**
+         * @hide
+         *
+         */
+        public static final String PARAMETER_PICTURE_QUALITY_EVENT_TYPE =
+                "picture_quality_event_type";
+
         private PictureQuality() {
         }
     }
@@ -641,6 +995,12 @@
          */
         public static final String PARAMETER_DIGITAL_OUTPUT_MODE = "digital_output_mode";
 
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_SOUND_STYLE = "sound_style";
+
+
 
         private SoundQuality() {
         }
diff --git a/media/java/android/mtp/OWNERS b/media/java/android/mtp/OWNERS
index 77ed08b..c57265a 100644
--- a/media/java/android/mtp/OWNERS
+++ b/media/java/android/mtp/OWNERS
@@ -1,9 +1,9 @@
 set noparent
 
-anothermark@google.com
+vmartensson@google.com
+nkapron@google.com
 febinthattil@google.com
-aprasath@google.com
+shubhankarm@google.com
 jsharkey@android.com
 jameswei@google.com
 rmojumder@google.com
-kumarashishg@google.com
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 8419ce7..3bc238a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -16,7 +16,9 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaCodec-JNI"
+#define ATRACE_TAG  ATRACE_TAG_VIDEO
 #include <utils/Log.h>
+#include <utils/Trace.h>
 
 #include <type_traits>
 
@@ -2106,7 +2108,7 @@
         jlong timestampUs,
         jint flags) {
     ALOGV("android_media_MediaCodec_queueInputBuffer");
-
+    ScopedTrace trace(ATRACE_TAG, "MediaCodec::queueInputBuffer#jni");
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
@@ -2192,6 +2194,7 @@
         jint index,
         jobjectArray objArray) {
     ALOGV("android_media_MediaCodec_queueInputBuffers");
+    ScopedTrace trace(ATRACE_TAG, "MediaCodec::queueInputBuffers#jni");
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
     if (codec == NULL || codec->initCheck() != OK || objArray == NULL) {
         throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
@@ -2431,6 +2434,7 @@
         jobject cryptoInfoObj,
         jlong timestampUs,
         jint flags) {
+    ScopedTrace trace(ATRACE_TAG, "MediaCodec::queueSecureInputBuffer#jni");
     ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
 
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
@@ -2641,6 +2645,7 @@
         jint index,
         jobjectArray bufferInfosObjs,
         jobjectArray cryptoInfoObjs) {
+    ScopedTrace trace(ATRACE_TAG, "MediaCodec::queueSecureInputBuffers#jni");
     ALOGV("android_media_MediaCodec_queueSecureInputBuffers");
 
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
@@ -2685,6 +2690,7 @@
 }
 
 static jobject android_media_MediaCodec_mapHardwareBuffer(JNIEnv *env, jclass, jobject bufferObj) {
+    ScopedTrace trace(ATRACE_TAG, "MediaCodec::mapHardwareBuffer#jni");
     ALOGV("android_media_MediaCodec_mapHardwareBuffer");
     AHardwareBuffer *hardwareBuffer = android_hardware_HardwareBuffer_getNativeHardwareBuffer(
             env, bufferObj);
@@ -3028,6 +3034,7 @@
 static void android_media_MediaCodec_native_queueLinearBlock(
         JNIEnv *env, jobject thiz, jint index, jobject bufferObj,
         jobjectArray cryptoInfoArray, jobjectArray objArray, jobject keys, jobject values) {
+    ScopedTrace trace(ATRACE_TAG, "MediaCodec::queueLinearBlock#jni");
     ALOGV("android_media_MediaCodec_native_queueLinearBlock");
 
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
@@ -3145,6 +3152,7 @@
         JNIEnv *env, jobject thiz, jint index, jobject bufferObj,
         jlong presentationTimeUs, jint flags, jobject keys, jobject values) {
     ALOGV("android_media_MediaCodec_native_queueHardwareBuffer");
+    ScopedTrace trace(ATRACE_TAG, "MediaCodec::queueHardwareBuffer#jni");
 
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index a77bc9f..a1ce495 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -910,7 +910,7 @@
         case MTP_FORMAT_TIFF:
         case MTP_FORMAT_TIFF_EP:
         case MTP_FORMAT_DEFINED: {
-            String8 temp(path);
+            String8 temp {static_cast<std::string_view>(path)};
             std::unique_ptr<FileStream> stream(new FileStream(temp));
             piex::PreviewImageData image_data;
             if (!GetExifFromRawImage(stream.get(), temp, image_data)) {
@@ -967,7 +967,7 @@
             case MTP_FORMAT_TIFF:
             case MTP_FORMAT_TIFF_EP:
             case MTP_FORMAT_DEFINED: {
-                String8 temp(path);
+                String8 temp {static_cast<std::string_view>(path)};
                 std::unique_ptr<FileStream> stream(new FileStream(temp));
                 piex::PreviewImageData image_data;
                 if (!GetExifFromRawImage(stream.get(), temp, image_data)) {
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
index e9a0d3e..209734c 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
@@ -16,6 +16,15 @@
 
 package com.android.audiopolicytest;
 
+import static android.media.AudioManager.STREAM_ACCESSIBILITY;
+import static android.media.AudioManager.STREAM_ALARM;
+import static android.media.AudioManager.STREAM_DTMF;
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_NOTIFICATION;
+import static android.media.AudioManager.STREAM_RING;
+import static android.media.AudioManager.STREAM_SYSTEM;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 
 import static com.android.audiopolicytest.AudioVolumeTestUtil.DEFAULT_ATTRIBUTES;
@@ -28,11 +37,15 @@
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Context;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
+import android.media.IAudioService;
 import android.media.audiopolicy.AudioProductStrategy;
 import android.media.audiopolicy.AudioVolumeGroup;
+import android.os.IBinder;
+import android.os.ServiceManager;
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
@@ -54,6 +67,17 @@
 
     private AudioManager mAudioManager;
 
+    private static final int[] PUBLIC_STREAM_TYPES = {
+            STREAM_VOICE_CALL,
+            STREAM_SYSTEM,
+            STREAM_RING,
+            STREAM_MUSIC,
+            STREAM_ALARM,
+            STREAM_NOTIFICATION,
+            STREAM_DTMF,
+            STREAM_ACCESSIBILITY,
+    };
+
     @Rule
     public final AudioVolumesTestRule rule = new AudioVolumesTestRule();
 
@@ -207,6 +231,33 @@
     }
 
     //-----------------------------------------------------------------
+    // Test getStreamVolume consistency with AudioService
+    //-----------------------------------------------------------------
+    @Test
+    public void getStreamMinMaxVolume_consistentWithAs() throws Exception {
+        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+        IAudioService service = IAudioService.Stub.asInterface(b);
+
+        for (int streamType : PUBLIC_STREAM_TYPES) {
+            assertEquals(service.getStreamMinVolume(streamType),
+                    mAudioManager.getStreamMinVolume(streamType));
+            assertEquals(service.getStreamMaxVolume(streamType),
+                    mAudioManager.getStreamMaxVolume(streamType));
+        }
+    }
+
+    @Test
+    public void getStreamVolume_consistentWithAs() throws Exception {
+        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+        IAudioService service = IAudioService.Stub.asInterface(b);
+
+        for (int streamType : PUBLIC_STREAM_TYPES) {
+            assertEquals(service.getStreamVolume(streamType),
+                    mAudioManager.getStreamVolume(streamType));
+        }
+    }
+
+    //-----------------------------------------------------------------
     // Test Volume per Attributes setter/getters
     //-----------------------------------------------------------------
     @Test
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index ec336d5..cf8e391 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -126,30 +126,6 @@
         }
     }
 
-    /** The camera2 api is only supported on HAL3.2+ devices */
-    @SmallTest
-    public void testSupportsCamera2Api() throws Exception {
-        for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
-            boolean supports = mUtils.getCameraService().supportsCameraApi(
-                String.valueOf(cameraId), API_VERSION_2);
-
-            Log.v(TAG, "Camera " + cameraId + " supports api2: " + supports);
-        }
-    }
-
-    /** The camera1 api is supported on *all* devices regardless of HAL version */
-    @SmallTest
-    public void testSupportsCamera1Api() throws Exception {
-        for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
-
-            boolean supports = mUtils.getCameraService().supportsCameraApi(
-                String.valueOf(cameraId), API_VERSION_1);
-            assertTrue(
-                    "Camera service returned false when queried if it supports camera1 api " +
-                    " for camera ID " + cameraId, supports);
-        }
-    }
-
     static abstract class DummyBase extends Binder implements android.os.IInterface {
         @Override
         public IBinder asBinder() {
diff --git a/media/tests/MtpTests/OWNERS b/media/tests/MtpTests/OWNERS
index bdb6cdb..c57265a 100644
--- a/media/tests/MtpTests/OWNERS
+++ b/media/tests/MtpTests/OWNERS
@@ -1,9 +1,9 @@
 set noparent
 
-anothermark@google.com
+vmartensson@google.com
+nkapron@google.com
 febinthattil@google.com
-aprasath@google.com
+shubhankarm@google.com
 jsharkey@android.com
 jameswei@google.com
 rmojumder@google.com
-kumarashishg@google.com
\ No newline at end of file
diff --git a/packages/ExtShared/Android.bp b/packages/ExtShared/Android.bp
index b1fd7f6..58016f7 100644
--- a/packages/ExtShared/Android.bp
+++ b/packages/ExtShared/Android.bp
@@ -38,6 +38,7 @@
     aaptflags: ["--shared-lib"],
     export_package_resources: true,
     optimize: {
+        keep_runtime_invisible_annotations: true,
         proguard_flags_files: ["proguard.proguard"],
     },
 }
diff --git a/packages/ExtShared/proguard.proguard b/packages/ExtShared/proguard.proguard
index e5dfbe1..699fbda 100644
--- a/packages/ExtShared/proguard.proguard
+++ b/packages/ExtShared/proguard.proguard
@@ -1,6 +1,8 @@
 -keepparameternames
 -keepattributes Exceptions,InnerClasses,Signature,Deprecated,
-                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
+                SourceFile,LineNumberTable,EnclosingMethod,
+                RuntimeVisibleAnnotations,RuntimeVisibleParameterAnnotations,
+                RuntimeVisibleTypeAnnotations,AnnotationDefault
 
 -keep public class * {
     public protected *;
diff --git a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.java b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.java
index cae8db2..428997e 100644
--- a/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.java
+++ b/packages/NeuralNetworks/framework/module/java/android/app/ondeviceintelligence/InferenceInfo.java
@@ -19,6 +19,7 @@
 import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE;
 
 import android.annotation.CurrentTimeMillisLong;
+import android.annotation.DurationMillisLong;
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
@@ -51,7 +52,8 @@
     private final long endTimeMs;
 
     /**
-     * Suspended time in milliseconds.
+     * The total duration of the period(s) during which the inference was
+     * suspended (i.e. not running), in milliseconds.
      */
     private final long suspendedTimeMs;
 
@@ -61,7 +63,7 @@
      * @param uid             Uid for the caller app.
      * @param startTimeMs     Inference start time (milliseconds from the epoch time).
      * @param endTimeMs       Inference end time (milliseconds from the epoch time).
-     * @param suspendedTimeMs Suspended time in milliseconds.
+     * @param suspendedTimeMs Suspended duration, in milliseconds.
      */
     InferenceInfo(int uid, long startTimeMs, long endTimeMs,
             long suspendedTimeMs) {
@@ -128,11 +130,12 @@
     }
 
     /**
-     * Returns the suspended time in milliseconds.
+     * Returns the suspended duration, in milliseconds.
      *
-     * @return the suspended time in milliseconds.
+     * @return the total duration of the period(s) during which the inference
+     *         was suspended (i.e. not running), in milliseconds.
      */
-    @CurrentTimeMillisLong
+    @DurationMillisLong
     public long getSuspendedTimeMillis() {
         return suspendedTimeMs;
     }
@@ -197,12 +200,14 @@
         }
 
         /**
-         * Sets the suspended time in milliseconds.
+         * Sets the suspended duration, in milliseconds.
          *
-         * @param suspendedTimeMs the suspended time in milliseconds.
+         * @param suspendedTimeMs the total duration of the period(s) in which
+         *                        the request was suspended (i.e. not running),
+        *                         in milliseconds.
          * @return the Builder instance.
          */
-        public @NonNull Builder setSuspendedTimeMillis(@CurrentTimeMillisLong long suspendedTimeMs) {
+        public @NonNull Builder setSuspendedTimeMillis(@DurationMillisLong long suspendedTimeMs) {
             this.suspendedTimeMs = suspendedTimeMs;
             return this;
         }
diff --git a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.java b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.java
index cae8db2..64524fb 100644
--- a/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.java
+++ b/packages/NeuralNetworks/framework/platform/java/android/app/ondeviceintelligence/InferenceInfo.java
@@ -19,6 +19,7 @@
 import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE;
 
 import android.annotation.CurrentTimeMillisLong;
+import android.annotation.DurationMillisLong;
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
@@ -50,8 +51,9 @@
      */
     private final long endTimeMs;
 
-    /**
-     * Suspended time in milliseconds.
+   /**
+     * The total duration of the period(s) during which the inference was
+     * suspended (i.e. not running), in milliseconds.
      */
     private final long suspendedTimeMs;
 
@@ -61,7 +63,7 @@
      * @param uid             Uid for the caller app.
      * @param startTimeMs     Inference start time (milliseconds from the epoch time).
      * @param endTimeMs       Inference end time (milliseconds from the epoch time).
-     * @param suspendedTimeMs Suspended time in milliseconds.
+     * @param suspendedTimeMs Suspended duration, in milliseconds.
      */
     InferenceInfo(int uid, long startTimeMs, long endTimeMs,
             long suspendedTimeMs) {
@@ -128,11 +130,12 @@
     }
 
     /**
-     * Returns the suspended time in milliseconds.
+     * Returns the suspended duration, in milliseconds.
      *
-     * @return the suspended time in milliseconds.
+     * @return the total duration of the period(s) during which the inference
+     *         was suspended (i.e. not running), in milliseconds.
      */
-    @CurrentTimeMillisLong
+    @DurationMillisLong
     public long getSuspendedTimeMillis() {
         return suspendedTimeMs;
     }
@@ -197,12 +200,14 @@
         }
 
         /**
-         * Sets the suspended time in milliseconds.
+         * Sets the suspended duration, in milliseconds.
          *
-         * @param suspendedTimeMs the suspended time in milliseconds.
+         * @param suspendedTimeMs the total duration of the period(s) in which
+         *                        the request was suspended (i.e. not running),
+        *                         in milliseconds.
          * @return the Builder instance.
          */
-        public @NonNull Builder setSuspendedTimeMillis(@CurrentTimeMillisLong long suspendedTimeMs) {
+        public @NonNull Builder setSuspendedTimeMillis(@DurationMillisLong long suspendedTimeMs) {
             this.suspendedTimeMs = suspendedTimeMs;
             return this;
         }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index fe27cee..acead8e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -510,7 +510,10 @@
                 protected Void doInBackground(Void... params) {
                     synchronized (mLock) {
                         try {
-                            if (mRenderer != null) {
+                            // A page count < 0 indicates there was an error
+                            // opening the document, in which case it doesn't
+                            // need to be closed.
+                            if (mRenderer != null && mPageCount >= 0) {
                                 mRenderer.closeDocument();
                             }
                         } catch (RemoteException re) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index b48c55d..a9d00e9 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -70,6 +70,7 @@
     private static final int STATE_CANCELING = 6;
     private static final int STATE_CANCELED = 7;
     private static final int STATE_DESTROYED = 8;
+    private static final int STATE_INVALID = 9;
 
     private final Context mContext;
 
@@ -287,7 +288,8 @@
         }
         if (mState != STATE_STARTED && mState != STATE_UPDATED
                 && mState != STATE_FAILED && mState != STATE_CANCELING
-                && mState != STATE_CANCELED && mState != STATE_DESTROYED) {
+                && mState != STATE_CANCELED && mState != STATE_DESTROYED
+                && mState != STATE_INVALID) {
             throw new IllegalStateException("Cannot finish in state:"
                     + stateToString(mState));
         }
@@ -300,6 +302,16 @@
         }
     }
 
+    /**
+     * Mark this document as invalid.
+     */
+    public void invalid() {
+        if (DEBUG) {
+            Log.i(LOG_TAG, "[CALLED] invalid()");
+        }
+        mState = STATE_INVALID;
+    }
+
     public void cancel(boolean force) {
         if (DEBUG) {
             Log.i(LOG_TAG, "[CALLED] cancel(" + force + ")");
@@ -491,6 +503,9 @@
             case STATE_DESTROYED: {
                 return "STATE_DESTROYED";
             }
+            case STATE_INVALID: {
+                return "STATE_INVALID";
+            }
             default: {
                 return "STATE_UNKNOWN";
             }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 4a3a6d2..2e3234e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -167,6 +167,7 @@
     private static final int STATE_PRINTER_UNAVAILABLE = 6;
     private static final int STATE_UPDATE_SLOW = 7;
     private static final int STATE_PRINT_COMPLETED = 8;
+    private static final int STATE_FILE_INVALID = 9;
 
     private static final int UI_STATE_PREVIEW = 0;
     private static final int UI_STATE_ERROR = 1;
@@ -404,6 +405,11 @@
     public void onPause() {
         PrintSpoolerService spooler = mSpoolerProvider.getSpooler();
 
+        if (isInvalid()) {
+            super.onPause();
+            return;
+        }
+
         if (mState == STATE_INITIALIZING) {
             if (isFinishing()) {
                 if (spooler != null) {
@@ -478,7 +484,8 @@
         }
 
         if (mState == STATE_PRINT_CANCELED || mState == STATE_PRINT_CONFIRMED
-                || mState == STATE_PRINT_COMPLETED) {
+                || mState == STATE_PRINT_COMPLETED
+                || mState == STATE_FILE_INVALID) {
             return true;
         }
 
@@ -509,23 +516,32 @@
     @Override
     public void onMalformedPdfFile() {
         onPrintDocumentError("Cannot print a malformed PDF file");
+        mPrintedDocument.invalid();
+        setState(STATE_FILE_INVALID);
     }
 
     @Override
     public void onSecurePdfFile() {
         onPrintDocumentError("Cannot print a password protected PDF file");
+        mPrintedDocument.invalid();
+        setState(STATE_FILE_INVALID);
     }
 
     private void onPrintDocumentError(String message) {
         setState(mProgressMessageController.cancel());
-        ensureErrorUiShown(null, PrintErrorFragment.ACTION_RETRY);
+        ensureErrorUiShown(
+                getString(R.string.print_cannot_load_page), PrintErrorFragment.ACTION_NONE);
 
         setState(STATE_UPDATE_FAILED);
         if (DEBUG) {
             Log.i(LOG_TAG, "PrintJob state[" +  PrintJobInfo.STATE_FAILED + "] reason: " + message);
         }
         PrintSpoolerService spooler = mSpoolerProvider.getSpooler();
-        spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_FAILED, message);
+        // Use a cancel state for the spooler.  This will prevent the notification from getting
+        // displayed and will remove the job.  The notification (which displays the cancel and
+        // restart options) doesn't make sense for an invalid document since it will just fail
+        // again.
+        spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_CANCELED, message);
         mPrintedDocument.finish();
     }
 
@@ -995,6 +1011,9 @@
     }
 
     private void setState(int state) {
+        if (isInvalid()) {
+            return;
+        }
         if (isFinalState(mState)) {
             if (isFinalState(state)) {
                 if (DEBUG) {
@@ -1015,7 +1034,12 @@
     private static boolean isFinalState(int state) {
         return state == STATE_PRINT_CANCELED
                 || state == STATE_PRINT_COMPLETED
-                || state == STATE_CREATE_FILE_FAILED;
+                || state == STATE_CREATE_FILE_FAILED
+                || state == STATE_FILE_INVALID;
+    }
+
+    private boolean isInvalid() {
+        return mState == STATE_FILE_INVALID;
     }
 
     private void updateSelectedPagesFromPreview() {
@@ -1100,7 +1124,7 @@
     }
 
     private void ensurePreviewUiShown() {
-        if (isFinishing() || isDestroyed()) {
+        if (isFinishing() || isDestroyed() || isInvalid()) {
             return;
         }
         if (mUiState != UI_STATE_PREVIEW) {
@@ -1257,6 +1281,9 @@
     }
 
     private boolean updateDocument(boolean clearLastError) {
+        if (isInvalid()) {
+            return false;
+        }
         if (!clearLastError && mPrintedDocument.hasUpdateError()) {
             return false;
         }
@@ -1676,7 +1703,8 @@
                 || mState == STATE_UPDATE_FAILED
                 || mState == STATE_CREATE_FILE_FAILED
                 || mState == STATE_PRINTER_UNAVAILABLE
-                || mState == STATE_UPDATE_SLOW) {
+                || mState == STATE_UPDATE_SLOW
+                || mState == STATE_FILE_INVALID) {
             disableOptionsUi(isFinalState(mState));
             return;
         }
@@ -2100,6 +2128,9 @@
     }
 
     private boolean canUpdateDocument() {
+        if (isInvalid()) {
+            return false;
+        }
         if (mPrintedDocument.isDestroyed()) {
             return false;
         }
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index 60a9ebd..c82829d 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -116,8 +116,8 @@
 
     // Default attention level is High.
     private AttentionLevel mAttentionLevel = AttentionLevel.HIGH;
-    private String mSubtitle;
-    private String mHeader;
+    private CharSequence mSubtitle;
+    private CharSequence mHeader;
     private int mButtonOrientation;
 
     public BannerMessagePreference(Context context) {
@@ -351,7 +351,7 @@
     /**
      * Sets the text to be displayed in positive button.
      */
-    public BannerMessagePreference setPositiveButtonText(String positiveButtonText) {
+    public BannerMessagePreference setPositiveButtonText(CharSequence positiveButtonText) {
         if (!TextUtils.equals(positiveButtonText, mPositiveButtonInfo.mText)) {
             mPositiveButtonInfo.mText = positiveButtonText;
             notifyChanged();
@@ -369,7 +369,7 @@
     /**
      * Sets the text to be displayed in negative button.
      */
-    public BannerMessagePreference setNegativeButtonText(String negativeButtonText) {
+    public BannerMessagePreference setNegativeButtonText(CharSequence negativeButtonText) {
         if (!TextUtils.equals(negativeButtonText, mNegativeButtonInfo.mText)) {
             mNegativeButtonInfo.mText = negativeButtonText;
             notifyChanged();
@@ -401,7 +401,7 @@
      * Sets the subtitle.
      */
     @RequiresApi(Build.VERSION_CODES.S)
-    public BannerMessagePreference setSubtitle(String subtitle) {
+    public BannerMessagePreference setSubtitle(CharSequence subtitle) {
         if (!TextUtils.equals(subtitle, mSubtitle)) {
             mSubtitle = subtitle;
             notifyChanged();
@@ -421,8 +421,8 @@
      * Sets the header.
      */
     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
-    public BannerMessagePreference setHeader(String header) {
-        if (!TextUtils.equals(header, mSubtitle)) {
+    public BannerMessagePreference setHeader(CharSequence header) {
+        if (!TextUtils.equals(header, mHeader)) {
             mHeader = header;
             notifyChanged();
         }
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml
index f55b320..ff22b2e 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml
@@ -20,7 +20,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:filterTouchesWhenObscured="true">
 
     <com.google.android.material.button.MaterialButton
         android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml
index b663b6c..d878ba0 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml
@@ -20,7 +20,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:filterTouchesWhenObscured="true">
 
     <com.google.android.material.button.MaterialButton
         android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml
index 784e6ad..8f0a158 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml
@@ -20,7 +20,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:filterTouchesWhenObscured="true">
 
     <com.google.android.material.button.MaterialButton
         android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml
index 8b44a65..0c89960 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml
@@ -20,7 +20,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:filterTouchesWhenObscured="true">
 
     <com.google.android.material.button.MaterialButton
         android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml
index f8a2d8f..41d8490 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml
@@ -20,7 +20,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:filterTouchesWhenObscured="true">
 
     <com.google.android.material.button.MaterialButton
         android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml
index 781a5a1..9585520 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml
@@ -20,7 +20,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:filterTouchesWhenObscured="true">
 
     <com.google.android.material.button.MaterialButton
         android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml
index 5b568f8..03ca1f0 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml
@@ -20,7 +20,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:filterTouchesWhenObscured="true">
 
     <com.google.android.material.button.MaterialButton
         android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml
index 1e7a08b..030ee66 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml
@@ -20,7 +20,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:filterTouchesWhenObscured="true">
 
     <com.google.android.material.button.MaterialButton
         android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml
index 42116be..5c16723 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml
@@ -20,7 +20,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:filterTouchesWhenObscured="true">
 
     <com.google.android.material.button.MaterialButton
         android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml
index 1ff0990..5405045 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml
@@ -20,7 +20,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:filterTouchesWhenObscured="true">
 
     <Button
         android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_number_button.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_number_button.xml
index fa13b41..b23c5a5 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_number_button.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_number_button.xml
@@ -29,7 +29,8 @@
         android:layout_height="wrap_content"
         android:paddingVertical="@dimen/settingslib_expressive_space_small1"
         android:paddingHorizontal="@dimen/settingslib_expressive_space_small4"
-        android:background="@drawable/settingslib_number_button_background">
+        android:background="@drawable/settingslib_number_button_background"
+        android:filterTouchesWhenObscured="true">
         <TextView
             android:id="@+id/settingslib_number_count"
             android:layout_width="wrap_content"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_section_button.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_section_button.xml
index e7fb572..66a4c2e 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_section_button.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_section_button.xml
@@ -21,7 +21,8 @@
     android:orientation="vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingVertical="@dimen/settingslib_expressive_space_extrasmall4">
+    android:paddingVertical="@dimen/settingslib_expressive_space_extrasmall4"
+    android:filterTouchesWhenObscured="true">
 
     <com.google.android.material.button.MaterialButton
         android:id="@+id/settingslib_section_button"
diff --git a/packages/SettingsLib/DisplayUtils/Android.bp b/packages/SettingsLib/DisplayUtils/Android.bp
index 279bb70..62630b5 100644
--- a/packages/SettingsLib/DisplayUtils/Android.bp
+++ b/packages/SettingsLib/DisplayUtils/Android.bp
@@ -15,6 +15,4 @@
     ],
 
     srcs: ["src/**/*.java"],
-
-    min_sdk_version: "21",
 }
diff --git a/packages/SettingsLib/DisplayUtils/src/com/android/settingslib/display/DisplayDensityConfiguration.java b/packages/SettingsLib/DisplayUtils/src/com/android/settingslib/display/DisplayDensityConfiguration.java
index 284a902..127e628 100644
--- a/packages/SettingsLib/DisplayUtils/src/com/android/settingslib/display/DisplayDensityConfiguration.java
+++ b/packages/SettingsLib/DisplayUtils/src/com/android/settingslib/display/DisplayDensityConfiguration.java
@@ -16,13 +16,20 @@
 
 package com.android.settingslib.display;
 
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
 import android.os.AsyncTask;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
+import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
+import java.util.function.Predicate;
+
 /** Utility methods for controlling the display density. */
 public class DisplayDensityConfiguration {
     private static final String LOG_TAG = "DisplayDensityConfig";
@@ -85,4 +92,42 @@
                     }
                 });
     }
+
+    /**
+     * Asynchronously applies display density changes to all displays that satisfy the predicate.
+     *
+     * <p>The change will be applied to the user specified by the value of
+     * {@link UserHandle#myUserId()} at the time the method is called.
+     *
+     * @param context The context
+     * @param predicate Determines which displays to set the density to
+     * @param density The density to force
+     */
+    public static void setForcedDisplayDensity(@NonNull Context context,
+            @NonNull Predicate<DisplayInfo> predicate, final int density) {
+        final int userId = UserHandle.myUserId();
+        DisplayManager dm = context.getSystemService(DisplayManager.class);
+        AsyncTask.execute(() -> {
+            try {
+                for (Display display : dm.getDisplays(
+                        DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
+                    int displayId = display.getDisplayId();
+                    DisplayInfo info = new DisplayInfo();
+                    if (!display.getDisplayInfo(info)) {
+                        Log.w(LOG_TAG, "Unable to save forced display density setting "
+                                + "for display " + displayId);
+                        continue;
+                    }
+                    if (!predicate.test(info)) {
+                        continue;
+                    }
+
+                    final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+                    wm.setForcedDisplayDensityForUser(displayId, density, userId);
+                }
+            } catch (RemoteException exc) {
+                Log.w(LOG_TAG, "Unable to save forced display density setting");
+            }
+        });
+    }
 }
diff --git a/packages/SettingsLib/Graph/graph.proto b/packages/SettingsLib/Graph/graph.proto
index 33a7df4..a834947 100644
--- a/packages/SettingsLib/Graph/graph.proto
+++ b/packages/SettingsLib/Graph/graph.proto
@@ -26,6 +26,14 @@
   optional PreferenceGroupProto root = 2;
   // If the preference screen provides complete hierarchy by source code.
   optional bool complete_hierarchy = 3;
+  // Parameterized screens (not recursive, provided on the top level only)
+  repeated ParameterizedPreferenceScreenProto parameterized_screens = 4;
+}
+
+// Proto of parameterized preference screen
+message ParameterizedPreferenceScreenProto {
+  optional BundleProto args = 1;
+  optional PreferenceScreenProto screen = 2;
 }
 
 // Proto of PreferenceGroup.
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
index adffd20..27ce1c7 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
@@ -18,12 +18,14 @@
 
 import android.app.Application
 import android.os.Bundle
+import android.os.Parcelable
 import android.os.SystemClock
 import com.android.settingslib.graph.proto.PreferenceGraphProto
 import com.android.settingslib.ipc.ApiHandler
 import com.android.settingslib.ipc.ApiPermissionChecker
 import com.android.settingslib.ipc.MessageCodec
 import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
+import com.android.settingslib.metadata.PreferenceScreenCoordinate
 import com.android.settingslib.metadata.PreferenceScreenRegistry
 import com.android.settingslib.preference.PreferenceScreenProvider
 import java.util.Locale
@@ -59,10 +61,9 @@
         var success = false
         try {
             val builder = PreferenceGraphBuilder.of(application, callingPid, callingUid, request)
-            if (request.screenKeys.isEmpty()) {
-                PreferenceScreenRegistry.preferenceScreenMetadataFactories.forEachKeyAsync {
-                    builder.addPreferenceScreenFromRegistry(it)
-                }
+            if (request.screens.isEmpty()) {
+                val factories = PreferenceScreenRegistry.preferenceScreenMetadataFactories
+                factories.forEachAsync { _, factory -> builder.addPreferenceScreen(factory) }
                 for (provider in preferenceScreenProviders) {
                     builder.addPreferenceScreenProvider(provider)
                 }
@@ -84,15 +85,15 @@
 /**
  * Request of [GetPreferenceGraphApiHandler].
  *
- * @param screenKeys screen keys of the preference graph
- * @param visitedScreens keys of the visited preference screen
+ * @param screens screens of the preference graph
+ * @param visitedScreens visited preference screens
  * @param locale locale of the preference graph
  */
 data class GetPreferenceGraphRequest
 @JvmOverloads
 constructor(
-    val screenKeys: Set<String> = setOf(),
-    val visitedScreens: Set<String> = setOf(),
+    val screens: Set<PreferenceScreenCoordinate> = setOf(),
+    val visitedScreens: Set<PreferenceScreenCoordinate> = setOf(),
     val locale: Locale? = null,
     val flags: Int = PreferenceGetterFlags.ALL,
     val includeValueDescriptor: Boolean = true,
@@ -101,26 +102,32 @@
 object GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> {
     override fun encode(data: GetPreferenceGraphRequest): Bundle =
         Bundle(4).apply {
-            putStringArray(KEY_SCREEN_KEYS, data.screenKeys.toTypedArray())
-            putStringArray(KEY_VISITED_KEYS, data.visitedScreens.toTypedArray())
+            putParcelableArray(KEY_SCREENS, data.screens.toTypedArray())
+            putParcelableArray(KEY_VISITED_SCREENS, data.visitedScreens.toTypedArray())
             putString(KEY_LOCALE, data.locale?.toLanguageTag())
             putInt(KEY_FLAGS, data.flags)
         }
 
+    @Suppress("DEPRECATION")
     override fun decode(data: Bundle): GetPreferenceGraphRequest {
-        val screenKeys = data.getStringArray(KEY_SCREEN_KEYS) ?: arrayOf()
-        val visitedScreens = data.getStringArray(KEY_VISITED_KEYS) ?: arrayOf()
+        data.classLoader = PreferenceScreenCoordinate::class.java.classLoader
+        val screens = data.getParcelableArray(KEY_SCREENS) ?: arrayOf()
+        val visitedScreens = data.getParcelableArray(KEY_VISITED_SCREENS) ?: arrayOf()
         fun String?.toLocale() = if (this != null) Locale.forLanguageTag(this) else null
+        fun Array<Parcelable>.toScreenCoordinates() =
+            buildSet(size) {
+                for (element in this@toScreenCoordinates) add(element as PreferenceScreenCoordinate)
+            }
         return GetPreferenceGraphRequest(
-            screenKeys.toSet(),
-            visitedScreens.toSet(),
+            screens.toScreenCoordinates(),
+            visitedScreens.toScreenCoordinates(),
             data.getString(KEY_LOCALE).toLocale(),
             data.getInt(KEY_FLAGS),
         )
     }
 
-    private const val KEY_SCREEN_KEYS = "k"
-    private const val KEY_VISITED_KEYS = "v"
+    private const val KEY_SCREENS = "s"
+    private const val KEY_VISITED_SCREENS = "v"
     private const val KEY_LOCALE = "l"
     private const val KEY_FLAGS = "f"
 }
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
index a9958b9..1d4e2c9e1 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
@@ -26,6 +26,7 @@
 import com.android.settingslib.metadata.PreferenceCoordinate
 import com.android.settingslib.metadata.PreferenceHierarchyNode
 import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
+import com.android.settingslib.metadata.PreferenceScreenCoordinate
 import com.android.settingslib.metadata.PreferenceScreenRegistry
 
 /**
@@ -105,8 +106,10 @@
         val errors = mutableMapOf<PreferenceCoordinate, Int>()
         val preferences = mutableMapOf<PreferenceCoordinate, PreferenceProto>()
         val flags = request.flags
-        for ((screenKey, coordinates) in request.preferences.groupBy { it.screenKey }) {
-            val screenMetadata = PreferenceScreenRegistry.create(application, screenKey)
+        val groups =
+            request.preferences.groupBy { PreferenceScreenCoordinate(it.screenKey, it.args) }
+        for ((screen, coordinates) in groups) {
+            val screenMetadata = PreferenceScreenRegistry.create(application, screen)
             if (screenMetadata == null) {
                 val latencyMs = SystemClock.elapsedRealtime() - elapsedRealtime
                 for (coordinate in coordinates) {
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index c0d2449..4290437 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -40,6 +40,7 @@
 import com.android.settingslib.graph.proto.PreferenceProto.ActionTarget
 import com.android.settingslib.graph.proto.PreferenceScreenProto
 import com.android.settingslib.graph.proto.TextProto
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
 import com.android.settingslib.metadata.IntRangeValuePreference
 import com.android.settingslib.metadata.PersistentPreference
 import com.android.settingslib.metadata.PreferenceAvailabilityProvider
@@ -47,7 +48,10 @@
 import com.android.settingslib.metadata.PreferenceMetadata
 import com.android.settingslib.metadata.PreferenceRestrictionProvider
 import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider
+import com.android.settingslib.metadata.PreferenceScreenCoordinate
 import com.android.settingslib.metadata.PreferenceScreenMetadata
+import com.android.settingslib.metadata.PreferenceScreenMetadataFactory
+import com.android.settingslib.metadata.PreferenceScreenMetadataParameterizedFactory
 import com.android.settingslib.metadata.PreferenceScreenRegistry
 import com.android.settingslib.metadata.PreferenceSummaryProvider
 import com.android.settingslib.metadata.PreferenceTitleProvider
@@ -72,15 +76,19 @@
         PreferenceScreenFactory(context.ofLocale(request.locale))
     }
     private val builder by lazy { PreferenceGraphProto.newBuilder() }
-    private val visitedScreens = mutableSetOf<String>().apply { addAll(request.visitedScreens) }
+    private val visitedScreens = request.visitedScreens.toMutableSet()
+    private val screens = mutableMapOf<String, PreferenceScreenProto.Builder>()
 
     private suspend fun init() {
-        for (key in request.screenKeys) {
-            addPreferenceScreenFromRegistry(key)
+        for (screen in request.screens) {
+            PreferenceScreenRegistry.create(context, screen)?.let { addPreferenceScreen(it) }
         }
     }
 
-    fun build(): PreferenceGraphProto = builder.build()
+    fun build(): PreferenceGraphProto {
+        for ((key, screenBuilder) in screens) builder.putScreens(key, screenBuilder.build())
+        return builder.build()
+    }
 
     /**
      * Adds an activity to the graph.
@@ -138,19 +146,12 @@
             null
         }
 
-    suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
-        val metadata = PreferenceScreenRegistry.create(context, key) ?: return false
-        return addPreferenceScreenMetadata(metadata)
+    private suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
+        val factory =
+            PreferenceScreenRegistry.preferenceScreenMetadataFactories[key] ?: return false
+        return addPreferenceScreen(factory)
     }
 
-    private suspend fun addPreferenceScreenMetadata(metadata: PreferenceScreenMetadata): Boolean =
-        addPreferenceScreen(metadata.key) {
-            preferenceScreenProto {
-                completeHierarchy = metadata.hasCompleteHierarchy()
-                root = metadata.getPreferenceHierarchy(context).toProto(metadata, true)
-            }
-        }
-
     suspend fun addPreferenceScreenProvider(activityClass: Class<*>) {
         Log.d(TAG, "add $activityClass")
         createPreferenceScreen { activityClass.newInstance() }
@@ -188,26 +189,52 @@
             Log.e(TAG, "\"$preferenceScreen\" has no key")
             return
         }
-        @Suppress("CheckReturnValue") addPreferenceScreen(key) { preferenceScreen.toProto(intent) }
+        val args = preferenceScreen.peekExtras()?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
+        @Suppress("CheckReturnValue")
+        addPreferenceScreen(key, args) {
+            this.intent = intent.toProto()
+            root = preferenceScreen.toProto()
+        }
     }
 
+    suspend fun addPreferenceScreen(factory: PreferenceScreenMetadataFactory): Boolean {
+        if (factory is PreferenceScreenMetadataParameterizedFactory) {
+            factory.parameters(context).collect { addPreferenceScreen(factory.create(context, it)) }
+            return true
+        }
+        return addPreferenceScreen(factory.create(context))
+    }
+
+    private suspend fun addPreferenceScreen(metadata: PreferenceScreenMetadata): Boolean =
+        addPreferenceScreen(metadata.key, metadata.arguments) {
+            completeHierarchy = metadata.hasCompleteHierarchy()
+            root = metadata.getPreferenceHierarchy(context).toProto(metadata, true)
+        }
+
     private suspend fun addPreferenceScreen(
         key: String,
-        preferenceScreenProvider: suspend () -> PreferenceScreenProto,
-    ): Boolean =
-        if (visitedScreens.add(key)) {
-            builder.putScreens(key, preferenceScreenProvider())
-            true
-        } else {
-            Log.w(TAG, "$key visited")
-            false
+        args: Bundle?,
+        init: suspend PreferenceScreenProto.Builder.() -> Unit,
+    ): Boolean {
+        if (!visitedScreens.add(PreferenceScreenCoordinate(key, args))) {
+            Log.w(TAG, "$key $args visited")
+            return false
         }
-
-    private suspend fun PreferenceScreen.toProto(intent: Intent?): PreferenceScreenProto =
-        preferenceScreenProto {
-            intent?.let { this.intent = it.toProto() }
-            root = (this@toProto as PreferenceGroup).toProto()
+        if (args == null) { // normal screen
+            screens[key] = PreferenceScreenProto.newBuilder().also { init(it) }
+        } else if (args.isEmpty) { // parameterized screen with backward compatibility
+            val builder = screens.getOrPut(key) { PreferenceScreenProto.newBuilder() }
+            init(builder)
+        } else { // parameterized screen with non-empty arguments
+            val builder = screens.getOrPut(key) { PreferenceScreenProto.newBuilder() }
+            val parameterizedScreen = parameterizedPreferenceScreenProto {
+                setArgs(args.toProto())
+                setScreen(PreferenceScreenProto.newBuilder().also { init(it) })
+            }
+            builder.addParameterizedScreens(parameterizedScreen)
         }
+        return true
+    }
 
     private suspend fun PreferenceGroup.toProto(): PreferenceGroupProto = preferenceGroupProto {
         preference = (this@toProto as Preference).toProto()
@@ -271,7 +298,7 @@
             .toProto(context, callingPid, callingUid, screenMetadata, isRoot, request.flags)
             .also {
                 if (metadata is PreferenceScreenMetadata) {
-                    @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata)
+                    @Suppress("CheckReturnValue") addPreferenceScreen(metadata)
                 }
                 metadata.intent(context)?.resolveActivity(context.packageManager)?.let {
                     if (it.packageName == context.packageName) {
@@ -322,7 +349,7 @@
                 val screenKey = screen?.key
                 if (!screenKey.isNullOrEmpty()) {
                     @Suppress("CheckReturnValue")
-                    addPreferenceScreen(screenKey) { screen.toProto(null) }
+                    addPreferenceScreen(screenKey, null) { root = screen.toProto() }
                     return actionTargetProto { key = screenKey }
                 }
             } catch (e: Exception) {
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
index a595f42..60f9c6b 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -40,11 +40,12 @@
 import com.android.settingslib.metadata.SensitivityLevel.Companion.UNKNOWN_SENSITIVITY
 
 /** Request to set preference value. */
-data class PreferenceSetterRequest(
-    val screenKey: String,
-    val key: String,
+class PreferenceSetterRequest(
+    screenKey: String,
+    args: Bundle?,
+    key: String,
     val value: PreferenceValueProto,
-)
+) : PreferenceCoordinate(screenKey, args, key)
 
 /** Result of preference setter request. */
 @IntDef(
@@ -121,7 +122,7 @@
             metricsLogger?.logSetterApi(
                 application,
                 callingUid,
-                PreferenceCoordinate(request.screenKey, request.key),
+                request,
                 null,
                 null,
                 PreferenceSetterResult.UNSUPPORTED,
@@ -130,7 +131,7 @@
             return PreferenceSetterResult.UNSUPPORTED
         }
         val screenMetadata =
-            PreferenceScreenRegistry.create(application, request.screenKey) ?: return notFound()
+            PreferenceScreenRegistry.create(application, request) ?: return notFound()
         val key = request.key
         val metadata =
             screenMetadata.getPreferenceHierarchy(application).find(key) ?: return notFound()
@@ -199,7 +200,7 @@
         metricsLogger?.logSetterApi(
             application,
             callingUid,
-            PreferenceCoordinate(request.screenKey, request.key),
+            request,
             screenMetadata,
             metadata,
             result,
@@ -235,6 +236,7 @@
     override fun encode(data: PreferenceSetterRequest) =
         Bundle(3).apply {
             putString(SCREEN_KEY, data.screenKey)
+            putBundle(ARGS, data.args)
             putString(KEY, data.key)
             putByteArray(null, data.value.toByteArray())
         }
@@ -242,10 +244,12 @@
     override fun decode(data: Bundle) =
         PreferenceSetterRequest(
             data.getString(SCREEN_KEY)!!,
+            data.getBundle(ARGS),
             data.getString(KEY)!!,
             PreferenceValueProto.parseFrom(data.getByteArray(null)!!),
         )
 
     private const val SCREEN_KEY = "s"
     private const val KEY = "k"
+    private const val ARGS = "a"
 }
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
index adbe773..5f2a0d8 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
@@ -19,6 +19,7 @@
 import com.android.settingslib.graph.proto.BundleProto
 import com.android.settingslib.graph.proto.BundleProto.BundleValue
 import com.android.settingslib.graph.proto.IntentProto
+import com.android.settingslib.graph.proto.ParameterizedPreferenceScreenProto
 import com.android.settingslib.graph.proto.PreferenceGroupProto
 import com.android.settingslib.graph.proto.PreferenceOrGroupProto
 import com.android.settingslib.graph.proto.PreferenceProto
@@ -39,6 +40,12 @@
     init: PreferenceScreenProto.Builder.() -> Unit
 ): PreferenceScreenProto = PreferenceScreenProto.newBuilder().also(init).build()
 
+/** Kotlin DSL-style builder for [PreferenceScreenProto]. */
+inline fun parameterizedPreferenceScreenProto(
+    init: ParameterizedPreferenceScreenProto.Builder.() -> Unit
+): ParameterizedPreferenceScreenProto =
+    ParameterizedPreferenceScreenProto.newBuilder().also(init).build()
+
 /** Returns preference or null. */
 val PreferenceOrGroupProto.preferenceOrNull
     get() = if (hasPreference()) preference else null
diff --git a/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml b/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
index 43cf6aa..7adcbf6 100644
--- a/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
+++ b/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
@@ -18,6 +18,8 @@
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/entity_header"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
     style="@style/SettingsLibEntityHeader">
 
     <LinearLayout
diff --git a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
index 38b6413..69b75ad 100644
--- a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
+++ b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
@@ -33,6 +33,9 @@
 /** Processor to gather preference screens annotated with `@ProvidePreferenceScreen`. */
 class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
     private val screens = mutableListOf<Screen>()
+    private val bundleType: TypeMirror by lazy {
+        processingEnv.elementUtils.getTypeElement("android.os.Bundle").asType()
+    }
     private val contextType: TypeMirror by lazy {
         processingEnv.elementUtils.getTypeElement("android.content.Context").asType()
     }
@@ -83,19 +86,57 @@
             error("@$ANNOTATION_NAME must be added to $PREFERENCE_SCREEN_METADATA subclass", this)
             return
         }
-        val constructorType = getConstructorType()
-        if (constructorType == null) {
+        fun reportConstructorError() =
             error(
-                "Class must be an object, or has single public constructor that " +
-                    "accepts no parameter or a Context parameter",
+                "Must have only one public constructor: constructor(), " +
+                    "constructor(Context), constructor(Bundle) or constructor(Context, Bundle)",
                 this,
             )
+        val constructor = findConstructor()
+        if (constructor == null || constructor.parameters.size > 2) {
+            reportConstructorError()
             return
         }
+        val constructorHasContextParameter = constructor.hasParameter(0, contextType)
+        var index = if (constructorHasContextParameter) 1 else 0
         val annotation = annotationMirrors.single { it.isElement(annotationElement) }
         val key = annotation.fieldValue<String>("value")!!
         val overlay = annotation.fieldValue<Boolean>("overlay") == true
-        screens.add(Screen(key, overlay, qualifiedName.toString(), constructorType))
+        val parameterized = annotation.fieldValue<Boolean>("parameterized") == true
+        var parametersHasContextParameter = false
+        if (parameterized) {
+            val parameters = findParameters()
+            if (parameters == null) {
+                error("require a static 'parameters()' or 'parameters(Context)' method", this)
+                return
+            }
+            parametersHasContextParameter = parameters
+            if (constructor.hasParameter(index, bundleType)) {
+                index++
+            } else {
+                error(
+                    "Parameterized screen constructor must be" +
+                        "constructor(Bundle) or constructor(Context, Bundle)",
+                    this,
+                )
+                return
+            }
+        }
+        if (index == constructor.parameters.size) {
+            screens.add(
+                Screen(
+                    key,
+                    overlay,
+                    parameterized,
+                    annotation.fieldValue<Boolean>("parameterizedMigration") == true,
+                    qualifiedName.toString(),
+                    constructorHasContextParameter,
+                    parametersHasContextParameter,
+                )
+            )
+        } else {
+            reportConstructorError()
+        }
     }
 
     private fun codegen() {
@@ -116,10 +157,15 @@
         screens.sort()
         processingEnv.filer.createSourceFile("$outputPkg.$outputClass").openWriter().use {
             it.write("package $outputPkg;\n\n")
+            it.write("import android.content.Context;\n")
+            it.write("import android.os.Bundle;\n")
             it.write("import $PACKAGE.FixedArrayMap;\n")
             it.write("import $PACKAGE.FixedArrayMap.OrderedInitializer;\n")
-            it.write("import $PACKAGE.$FACTORY;\n\n")
-            it.write("// Generated by annotation processor for @$ANNOTATION_NAME\n")
+            it.write("import $PACKAGE.$PREFERENCE_SCREEN_METADATA;\n")
+            it.write("import $PACKAGE.$FACTORY;\n")
+            it.write("import $PACKAGE.$PARAMETERIZED_FACTORY;\n")
+            it.write("import kotlinx.coroutines.flow.Flow;\n")
+            it.write("\n// Generated by annotation processor for @$ANNOTATION_NAME\n")
             it.write("public final class $outputClass {\n")
             it.write("  private $outputClass() {}\n\n")
             it.write("  public static FixedArrayMap<String, $FACTORY> $outputFun() {\n")
@@ -127,10 +173,29 @@
             it.write("    return new FixedArrayMap<>($size, $outputClass::init);\n")
             it.write("  }\n\n")
             fun Screen.write() {
-                it.write("    screens.put(\"$key\", context -> new $klass(")
-                when (constructorType) {
-                    ConstructorType.DEFAULT -> it.write("));")
-                    ConstructorType.CONTEXT -> it.write("context));")
+                it.write("    screens.put(\"$key\", ")
+                if (parameterized) {
+                    it.write("new $PARAMETERIZED_FACTORY() {\n")
+                    it.write("      @Override public PreferenceScreenMetadata create")
+                    it.write("(Context context, Bundle args) {\n")
+                    it.write("        return new $klass(")
+                    if (constructorHasContextParameter) it.write("context, ")
+                    it.write("args);\n")
+                    it.write("      }\n\n")
+                    it.write("      @Override public Flow<Bundle> parameters(Context context) {\n")
+                    it.write("        return $klass.parameters(")
+                    if (parametersHasContextParameter) it.write("context")
+                    it.write(");\n")
+                    it.write("      }\n")
+                    if (parameterizedMigration) {
+                        it.write("\n      @Override public boolean acceptEmptyArguments()")
+                        it.write(" { return true; }\n")
+                    }
+                    it.write("    });")
+                } else {
+                    it.write("context -> new $klass(")
+                    if (constructorHasContextParameter) it.write("context")
+                    it.write("));")
                 }
                 if (overlay) it.write(" // overlay")
                 it.write("\n")
@@ -159,7 +224,7 @@
     }
 
     private fun AnnotationMirror.isElement(element: TypeElement) =
-        processingEnv.typeUtils.isSameType(annotationType.asElement().asType(), element.asType())
+        annotationType.asElement().asType().isSameType(element.asType())
 
     @Suppress("UNCHECKED_CAST")
     private fun <T> AnnotationMirror.fieldValue(name: String): T? = field(name)?.value as? T
@@ -171,7 +236,7 @@
         return null
     }
 
-    private fun TypeElement.getConstructorType(): ConstructorType? {
+    private fun TypeElement.findConstructor(): ExecutableElement? {
         var constructor: ExecutableElement? = null
         for (element in enclosedElements) {
             if (element.kind != ElementKind.CONSTRUCTOR) continue
@@ -179,16 +244,30 @@
             if (constructor != null) return null
             constructor = element as ExecutableElement
         }
-        return constructor?.parameters?.run {
-            when {
-                isEmpty() -> ConstructorType.DEFAULT
-                size == 1 && processingEnv.typeUtils.isSameType(this[0].asType(), contextType) ->
-                    ConstructorType.CONTEXT
-                else -> null
-            }
-        }
+        return constructor
     }
 
+    private fun TypeElement.findParameters(): Boolean? {
+        for (element in enclosedElements) {
+            if (element.kind != ElementKind.METHOD) continue
+            if (!element.modifiers.contains(Modifier.PUBLIC)) continue
+            if (!element.modifiers.contains(Modifier.STATIC)) continue
+            if (!element.simpleName.contentEquals("parameters")) return null
+            val parameters = (element as ExecutableElement).parameters
+            if (parameters.isEmpty()) return false
+            if (parameters.size == 1 && parameters[0].asType().isSameType(contextType)) return true
+            error("parameters method should have no parameter or a Context parameter", element)
+            return null
+        }
+        return null
+    }
+
+    private fun ExecutableElement.hasParameter(index: Int, typeMirror: TypeMirror) =
+        index < parameters.size && parameters[index].asType().isSameType(typeMirror)
+
+    private fun TypeMirror.isSameType(typeMirror: TypeMirror) =
+        processingEnv.typeUtils.isSameType(this, typeMirror)
+
     private fun warn(msg: CharSequence) =
         processingEnv.messager.printMessage(Diagnostic.Kind.WARNING, msg)
 
@@ -198,8 +277,11 @@
     private data class Screen(
         val key: String,
         val overlay: Boolean,
+        val parameterized: Boolean,
+        val parameterizedMigration: Boolean,
         val klass: String,
-        val constructorType: ConstructorType,
+        val constructorHasContextParameter: Boolean,
+        val parametersHasContextParameter: Boolean,
     ) : Comparable<Screen> {
         override fun compareTo(other: Screen): Int {
             val diff = key.compareTo(other.key)
@@ -207,17 +289,13 @@
         }
     }
 
-    private enum class ConstructorType {
-        DEFAULT, // default constructor with no parameter
-        CONTEXT, // constructor with a Context parameter
-    }
-
     companion object {
         private const val PACKAGE = "com.android.settingslib.metadata"
         private const val ANNOTATION_NAME = "ProvidePreferenceScreen"
         private const val ANNOTATION = "$PACKAGE.$ANNOTATION_NAME"
         private const val PREFERENCE_SCREEN_METADATA = "PreferenceScreenMetadata"
         private const val FACTORY = "PreferenceScreenMetadataFactory"
+        private const val PARAMETERIZED_FACTORY = "PreferenceScreenMetadataParameterizedFactory"
 
         private const val OPTIONS_NAME = "ProvidePreferenceScreenOptions"
         private const val OPTIONS = "$PACKAGE.$OPTIONS_NAME"
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
index 4bed795..449c78c 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
@@ -22,14 +22,27 @@
  * The annotated class must satisfy either condition:
  * - the primary constructor has no parameter
  * - the primary constructor has a single [android.content.Context] parameter
+ * - (parameterized) the primary constructor has a single [android.os.Bundle] parameter to override
+ *   [PreferenceScreenMetadata.arguments]
+ * - (parameterized) the primary constructor has a [android.content.Context] and a
+ *   [android.os.Bundle] parameter to override [PreferenceScreenMetadata.arguments]
  *
  * @param value unique preference screen key
  * @param overlay if true, current annotated screen will overlay the screen that has identical key
+ * @param parameterized if true, the screen relies on additional arguments to build its content
+ * @param parameterizedMigration whether the parameterized screen was a normal screen, in which case
+ *   `Bundle.EMPTY` will be passed as arguments to take care of backward compatibility
+ * @see PreferenceScreenMetadata
  */
 @Retention(AnnotationRetention.SOURCE)
 @Target(AnnotationTarget.CLASS)
 @MustBeDocumented
-annotation class ProvidePreferenceScreen(val value: String, val overlay: Boolean = false)
+annotation class ProvidePreferenceScreen(
+    val value: String,
+    val overlay: Boolean = false,
+    val parameterized: Boolean = false,
+    val parameterizedMigration: Boolean = false, // effective only when parameterized is true
+)
 
 /**
  * Provides options for [ProvidePreferenceScreen] annotation processor.
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Bundles.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Bundles.kt
new file mode 100644
index 0000000..a6357651
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Bundles.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 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.settingslib.metadata
+
+import android.content.Intent
+import android.os.Bundle
+
+@Suppress("DEPRECATION")
+fun Bundle?.contentEquals(other: Bundle?): Boolean {
+    if (this == null) return other == null
+    if (other == null) return false
+    if (keySet() != other.keySet()) return false
+    fun Any?.valueEquals(other: Any?) =
+        when (this) {
+            is Bundle -> other is Bundle && this.contentEquals(other)
+            is Intent -> other is Intent && this.filterEquals(other)
+            is BooleanArray -> other is BooleanArray && this contentEquals other
+            is ByteArray -> other is ByteArray && this contentEquals other
+            is CharArray -> other is CharArray && this contentEquals other
+            is DoubleArray -> other is DoubleArray && this contentEquals other
+            is FloatArray -> other is FloatArray && this contentEquals other
+            is IntArray -> other is IntArray && this contentEquals other
+            is LongArray -> other is LongArray && this contentEquals other
+            is ShortArray -> other is ShortArray && this contentEquals other
+            is Array<*> -> other is Array<*> && this contentDeepEquals other
+            else -> this == other
+        }
+    for (key in keySet()) {
+        if (!get(key).valueEquals(other.get(key))) return false
+    }
+    return true
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceCoordinate.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceCoordinate.kt
index 2dd736a..ac08847 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceCoordinate.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceCoordinate.kt
@@ -16,26 +16,41 @@
 
 package com.android.settingslib.metadata
 
+import android.os.Bundle
 import android.os.Parcel
 import android.os.Parcelable
 
 /**
  * Coordinate to locate a preference.
  *
- * Within an app, the preference screen key (unique among screens) plus preference key (unique on
- * the screen) is used to locate a preference.
+ * Within an app, the preference screen coordinate (unique among screens) plus preference key
+ * (unique on the screen) is used to locate a preference.
  */
-data class PreferenceCoordinate(val screenKey: String, val key: String) : Parcelable {
+open class PreferenceCoordinate : PreferenceScreenCoordinate {
+    val key: String
 
-    constructor(parcel: Parcel) : this(parcel.readString()!!, parcel.readString()!!)
+    constructor(screenKey: String, key: String) : this(screenKey, null, key)
+
+    constructor(screenKey: String, args: Bundle?, key: String) : super(screenKey, args) {
+        this.key = key
+    }
+
+    constructor(parcel: Parcel) : super(parcel) {
+        this.key = parcel.readString()!!
+    }
 
     override fun writeToParcel(parcel: Parcel, flags: Int) {
-        parcel.writeString(screenKey)
+        super.writeToParcel(parcel, flags)
         parcel.writeString(key)
     }
 
     override fun describeContents() = 0
 
+    override fun equals(other: Any?) =
+        super.equals(other) && key == (other as PreferenceCoordinate).key
+
+    override fun hashCode() = super.hashCode() xor key.hashCode()
+
     companion object CREATOR : Parcelable.Creator<PreferenceCoordinate> {
 
         override fun createFromParcel(parcel: Parcel) = PreferenceCoordinate(parcel)
@@ -43,3 +58,46 @@
         override fun newArray(size: Int) = arrayOfNulls<PreferenceCoordinate>(size)
     }
 }
+
+/** Coordinate to locate a preference screen. */
+open class PreferenceScreenCoordinate : Parcelable {
+    /** Unique preference screen key. */
+    val screenKey: String
+
+    /** Arguments to create parameterized preference screen. */
+    val args: Bundle?
+
+    constructor(screenKey: String, args: Bundle?) {
+        this.screenKey = screenKey
+        this.args = args
+    }
+
+    constructor(parcel: Parcel) {
+        screenKey = parcel.readString()!!
+        args = parcel.readBundle(javaClass.classLoader)
+    }
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.writeString(screenKey)
+        parcel.writeBundle(args)
+    }
+
+    override fun describeContents() = 0
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+        other as PreferenceScreenCoordinate
+        return screenKey == other.screenKey && args.contentEquals(other.args)
+    }
+
+    // "args" is not included intentionally, otherwise we need to take care of array, etc.
+    override fun hashCode() = screenKey.hashCode()
+
+    companion object CREATOR : Parcelable.Creator<PreferenceScreenCoordinate> {
+
+        override fun createFromParcel(parcel: Parcel) = PreferenceScreenCoordinate(parcel)
+
+        override fun newArray(size: Int) = arrayOfNulls<PreferenceScreenCoordinate>(size)
+    }
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
index 876f615..3bd051d 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -17,6 +17,7 @@
 package com.android.settingslib.metadata
 
 import android.content.Context
+import android.os.Bundle
 
 /** A node in preference hierarchy that is associated with [PreferenceMetadata]. */
 open class PreferenceHierarchyNode internal constructor(val metadata: PreferenceMetadata) {
@@ -54,8 +55,14 @@
      *
      * @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
      */
-    operator fun String.unaryPlus() =
-        +PreferenceHierarchyNode(PreferenceScreenRegistry.create(context, this)!!)
+    operator fun String.unaryPlus() = addPreferenceScreen(this, null)
+
+    /**
+     * Adds parameterized preference screen with given key (as a placeholder) to the hierarchy.
+     *
+     * @see String.unaryPlus
+     */
+    infix fun String.args(args: Bundle) = createPreferenceScreenHierarchy(this, args)
 
     operator fun PreferenceHierarchyNode.unaryPlus() = also { children.add(it) }
 
@@ -122,6 +129,14 @@
         }
 
     /**
+     * Adds parameterized preference screen with given key (as a placeholder) to the hierarchy.
+     *
+     * @see addPreferenceScreen
+     */
+    fun addParameterizedScreen(screenKey: String, args: Bundle) =
+        addPreferenceScreen(screenKey, args)
+
+    /**
      * Adds preference screen with given key (as a placeholder) to the hierarchy.
      *
      * This is mainly to support Android Settings overlays. OEMs might want to custom some of the
@@ -132,11 +147,13 @@
      *
      * @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
      */
-    fun addPreferenceScreen(screenKey: String) {
-        children.add(
-            PreferenceHierarchy(context, PreferenceScreenRegistry.create(context, screenKey)!!)
-        )
-    }
+    fun addPreferenceScreen(screenKey: String) = addPreferenceScreen(screenKey, null)
+
+    private fun addPreferenceScreen(screenKey: String, args: Bundle?): PreferenceHierarchyNode =
+        createPreferenceScreenHierarchy(screenKey, args).also { children.add(it) }
+
+    private fun createPreferenceScreenHierarchy(screenKey: String, args: Bundle?) =
+        PreferenceHierarchyNode(PreferenceScreenRegistry.create(context, screenKey, args)!!)
 
     /** Extensions to add more preferences to the hierarchy. */
     operator fun PreferenceHierarchy.plusAssign(init: PreferenceHierarchy.() -> Unit) = init(this)
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
index 84014f1..4fd13ed 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
@@ -17,13 +17,20 @@
 package com.android.settingslib.metadata
 
 import android.content.Context
+import android.os.Bundle
 
 /** Provides the associated preference screen key for binding. */
 interface PreferenceScreenBindingKeyProvider {
 
     /** Returns the associated preference screen key. */
     fun getPreferenceScreenBindingKey(context: Context): String?
+
+    /** Returns the arguments to build preference screen. */
+    fun getPreferenceScreenBindingArgs(context: Context): Bundle?
 }
 
 /** Extra key to provide the preference screen key for binding. */
 const val EXTRA_BINDING_SCREEN_KEY = "settingslib:binding_screen_key"
+
+/** Extra key to provide arguments for preference screen binding. */
+const val EXTRA_BINDING_SCREEN_ARGS = "settingslib:binding_screen_args"
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
index 850d452..7f1ded7 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
@@ -18,12 +18,25 @@
 
 import android.content.Context
 import android.content.Intent
+import android.os.Bundle
 import androidx.annotation.AnyThread
 import androidx.fragment.app.Fragment
+import kotlinx.coroutines.flow.Flow
 
-/** Metadata of preference screen. */
+/**
+ * Metadata of preference screen.
+ *
+ * For parameterized preference screen that relies on additional information (e.g. package name,
+ * language code) to build its content, the subclass must:
+ * - override [arguments] in constructor
+ * - add a static method `fun parameters(context: Context): List<Bundle>` (context is optional) to
+ *   provide all possible arguments
+ */
 @AnyThread
 interface PreferenceScreenMetadata : PreferenceMetadata {
+    /** Arguments to build the screen content. */
+    val arguments: Bundle?
+        get() = null
 
     /**
      * The screen title resource, which precedes [getScreenTitle] if provided.
@@ -65,7 +78,12 @@
     fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?): Intent? = null
 }
 
-/** Factory of [PreferenceScreenMetadata]. */
+/**
+ * Factory of [PreferenceScreenMetadata].
+ *
+ * Annotation processor generates implementation of this interface based on
+ * [ProvidePreferenceScreen] when [ProvidePreferenceScreen.parameterized] is `false`.
+ */
 fun interface PreferenceScreenMetadataFactory {
 
     /**
@@ -75,3 +93,44 @@
      */
     fun create(context: Context): PreferenceScreenMetadata
 }
+
+/**
+ * Parameterized factory of [PreferenceScreenMetadata].
+ *
+ * Annotation processor generates implementation of this interface based on
+ * [ProvidePreferenceScreen] when [ProvidePreferenceScreen.parameterized] is `true`.
+ */
+interface PreferenceScreenMetadataParameterizedFactory : PreferenceScreenMetadataFactory {
+    override fun create(context: Context) = create(context, Bundle.EMPTY)
+
+    /**
+     * Creates a new [PreferenceScreenMetadata] with given arguments.
+     *
+     * @param context application context to create the PreferenceScreenMetadata
+     * @param args arguments to create the screen metadata, [Bundle.EMPTY] is reserved for the
+     *   default case when screen is migrated from normal to parameterized
+     */
+    fun create(context: Context, args: Bundle): PreferenceScreenMetadata
+
+    /**
+     * Returns all possible arguments to create [PreferenceScreenMetadata].
+     *
+     * Note that [Bundle.EMPTY] is a special arguments reserved for backward compatibility when a
+     * preference screen was a normal screen but migrated to parameterized screen later:
+     * 1. Set [ProvidePreferenceScreen.parameterizedMigration] to `true`, so that the generated
+     *    [acceptEmptyArguments] will be `true`.
+     * 1. In the original [parameters] implementation, produce a [Bundle.EMPTY] for the default
+     *    case.
+     *
+     * Do not use [Bundle.EMPTY] for other purpose.
+     */
+    fun parameters(context: Context): Flow<Bundle>
+
+    /**
+     * Returns true when the parameterized screen was a normal screen.
+     *
+     * The [PreferenceScreenMetadata] is expected to accept an empty arguments ([Bundle.EMPTY]) and
+     * take care of backward compatibility.
+     */
+    fun acceptEmptyArguments(): Boolean = false
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
index c74b315..2463109 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
@@ -17,10 +17,13 @@
 package com.android.settingslib.metadata
 
 import android.content.Context
+import android.os.Bundle
+import android.util.Log
 import com.android.settingslib.datastore.KeyValueStore
 
 /** Registry of all available preference screens in the app. */
 object PreferenceScreenRegistry : ReadWritePermitProvider {
+    private const val TAG = "ScreenRegistry"
 
     /** Provider of key-value store. */
     private lateinit var keyValueStoreProvider: KeyValueStoreProvider
@@ -52,9 +55,28 @@
     fun getKeyValueStore(context: Context, preference: PreferenceMetadata): KeyValueStore? =
         keyValueStoreProvider.getKeyValueStore(context, preference)
 
-    /** Creates [PreferenceScreenMetadata] of particular screen key. */
-    fun create(context: Context, screenKey: String?): PreferenceScreenMetadata? =
-        screenKey?.let { preferenceScreenMetadataFactories[it]?.create(context.applicationContext) }
+    /** Creates [PreferenceScreenMetadata] of particular screen. */
+    fun create(context: Context, screenCoordinate: PreferenceScreenCoordinate) =
+        create(context, screenCoordinate.screenKey, screenCoordinate.args)
+
+    /** Creates [PreferenceScreenMetadata] of particular screen key with given arguments. */
+    fun create(context: Context, screenKey: String?, args: Bundle?): PreferenceScreenMetadata? {
+        if (screenKey == null) return null
+        val factory = preferenceScreenMetadataFactories[screenKey] ?: return null
+        val appContext = context.applicationContext
+        if (factory is PreferenceScreenMetadataParameterizedFactory) {
+            if (args != null) return factory.create(appContext, args)
+            // In case the parameterized screen was a normal scree, it is expected to accept
+            // Bundle.EMPTY arguments and take care of backward compatibility.
+            if (factory.acceptEmptyArguments()) return factory.create(appContext)
+            Log.e(TAG, "screen $screenKey is parameterized but args is not provided")
+            return null
+        } else {
+            if (args == null) return factory.create(appContext)
+            Log.e(TAG, "screen $screenKey is not parameterized but args is provided")
+            return null
+        }
+    }
 
     /**
      * Sets the provider to check read write permit. Read and write requests are denied by default.
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
index 65fbe2b..dbac17d 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
@@ -22,6 +22,7 @@
 import androidx.preference.PreferenceScreen
 import androidx.preference.SwitchPreferenceCompat
 import androidx.preference.TwoStatePreference
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
 import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
 import com.android.settingslib.metadata.PreferenceMetadata
 import com.android.settingslib.metadata.PreferenceScreenMetadata
@@ -35,9 +36,11 @@
         super.bind(preference, metadata)
         val context = preference.context
         val screenMetadata = metadata as PreferenceScreenMetadata
+        val extras = preference.extras
         // Pass the preference key to fragment, so that the fragment could find associated
         // preference screen registered in PreferenceScreenRegistry
-        preference.extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
+        extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
+        screenMetadata.arguments?.let { extras.putBundle(EXTRA_BINDING_SCREEN_ARGS, it) }
         if (preference is PreferenceScreen) {
             val screenTitle = screenMetadata.screenTitle
             preference.title =
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
index ffe181d..02f91c1 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
@@ -23,6 +23,7 @@
 import androidx.annotation.XmlRes
 import androidx.lifecycle.Lifecycle
 import androidx.preference.PreferenceScreen
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
 import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
 import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider
 import com.android.settingslib.metadata.PreferenceScreenRegistry
@@ -89,13 +90,19 @@
     @XmlRes protected open fun getPreferenceScreenResId(context: Context): Int = 0
 
     protected fun getPreferenceScreenCreator(context: Context): PreferenceScreenCreator? =
-        (PreferenceScreenRegistry.create(context, getPreferenceScreenBindingKey(context))
-                as? PreferenceScreenCreator)
+        (PreferenceScreenRegistry.create(
+                context,
+                getPreferenceScreenBindingKey(context),
+                getPreferenceScreenBindingArgs(context),
+            ) as? PreferenceScreenCreator)
             ?.run { if (isFlagEnabled(context)) this else null }
 
     override fun getPreferenceScreenBindingKey(context: Context): String? =
         arguments?.getString(EXTRA_BINDING_SCREEN_KEY)
 
+    override fun getPreferenceScreenBindingArgs(context: Context): Bundle? =
+        arguments?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         preferenceScreenBindingHelper?.onCreate()
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 4a6a589..1cb8005 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -31,6 +31,7 @@
 import com.android.settingslib.datastore.KeyedDataObservable
 import com.android.settingslib.datastore.KeyedObservable
 import com.android.settingslib.datastore.KeyedObserver
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
 import com.android.settingslib.metadata.PersistentPreference
 import com.android.settingslib.metadata.PreferenceChangeReason
 import com.android.settingslib.metadata.PreferenceHierarchy
@@ -227,14 +228,16 @@
         /** Updates preference screen that has incomplete hierarchy. */
         @JvmStatic
         fun bind(preferenceScreen: PreferenceScreen) {
-            PreferenceScreenRegistry.create(preferenceScreen.context, preferenceScreen.key)?.run {
+            val context = preferenceScreen.context
+            val args = preferenceScreen.peekExtras()?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
+            PreferenceScreenRegistry.create(context, preferenceScreen.key, args)?.run {
                 if (!hasCompleteHierarchy()) {
                     val preferenceBindingFactory =
                         (this as? PreferenceScreenCreator)?.preferenceBindingFactory ?: return
                     bindRecursively(
                         preferenceScreen,
                         preferenceBindingFactory,
-                        getPreferenceHierarchy(preferenceScreen.context),
+                        getPreferenceHierarchy(context),
                     )
                 }
             }
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
index 211b3bd..88c4fe6 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
@@ -17,10 +17,12 @@
 package com.android.settingslib.preference
 
 import android.content.Context
+import android.os.Bundle
 import androidx.preference.Preference
 import androidx.preference.PreferenceFragmentCompat
 import androidx.preference.PreferenceManager
 import androidx.preference.PreferenceScreen
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
 import com.android.settingslib.metadata.PreferenceScreenRegistry
 
 /** Factory to create preference screen. */
@@ -81,8 +83,12 @@
      *
      * The screen must be registered in [PreferenceScreenFactory] and provide a complete hierarchy.
      */
-    fun createBindingScreen(context: Context, screenKey: String?): PreferenceScreen? {
-        val metadata = PreferenceScreenRegistry.create(context, screenKey) ?: return null
+    fun createBindingScreen(
+        context: Context,
+        screenKey: String?,
+        args: Bundle?,
+    ): PreferenceScreen? {
+        val metadata = PreferenceScreenRegistry.create(context, screenKey, args) ?: return null
         if (metadata is PreferenceScreenCreator && metadata.hasCompleteHierarchy()) {
             return metadata.createPreferenceScreen(this)
         }
@@ -94,8 +100,9 @@
         @JvmStatic
         fun createBindingScreen(preference: Preference): PreferenceScreen? {
             val context = preference.context
+            val args = preference.peekExtras()?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
             val preferenceScreenCreator =
-                (PreferenceScreenRegistry.create(context, preference.key)
+                (PreferenceScreenRegistry.create(context, preference.key, args)
                     as? PreferenceScreenCreator) ?: return null
             if (!preferenceScreenCreator.hasCompleteHierarchy()) return null
             val factory = PreferenceScreenFactory(context)
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml
index eaf5bcd..30ed347 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml
@@ -21,7 +21,7 @@
     <string name="menu_show_system" msgid="906304605807554788">"نمایش سیستم"</string>
     <string name="menu_hide_system" msgid="374571689914923020">"پنهان کردن سیستم"</string>
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"مجاز"</string>
-    <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"مجاز نبودن"</string>
+    <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"غیرمجاز"</string>
     <string name="version_text" msgid="4001669804596458577">"نسخه <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
     <string name="cloned_app_info_label" msgid="1765651167024478391">"همسانه <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index 3309faa..3a63279 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -32,6 +32,7 @@
 data class EnhancedConfirmation(
     val key: String,
     val packageName: String,
+    val isRestrictedSettingAllowed: Boolean?
 )
 data class Restrictions(
     val userId: Int = UserHandle.myUserId(),
@@ -92,6 +93,9 @@
         }
 
         restrictions.enhancedConfirmation?.let { ec ->
+            if (ec.isRestrictedSettingAllowed == true) {
+                return NoRestricted
+            }
             RestrictedLockUtilsInternal
                     .checkIfRequiresEnhancedConfirmation(context, ec.key,
                         ec.packageName)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 7466f95..5580d2e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -155,7 +155,7 @@
         }
         RestrictedSwitchPreference(
             model = switchModel,
-            restrictions = getRestrictions(userId, packageName),
+            restrictions = getRestrictions(userId, packageName, isAllowed()),
             ifBlockedByAdminOverrideCheckedValueTo = switchifBlockedByAdminOverrideCheckedValueTo,
             restrictionsProviderFactory = restrictionsProviderFactory,
         )
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
index d2867af1..771eb85 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
@@ -116,12 +116,15 @@
 fun <T : AppRecord> TogglePermissionAppListModel<T>.getRestrictions(
     userId: Int,
     packageName: String,
+    isRestrictedSettingAllowed: Boolean?
 ) =
     Restrictions(
         userId = userId,
         keys = switchRestrictionKeys,
         enhancedConfirmation =
-            enhancedConfirmationKey?.let { key -> EnhancedConfirmation(key, packageName) },
+            enhancedConfirmationKey?.let {
+                key -> EnhancedConfirmation(key, packageName, isRestrictedSettingAllowed)
+                                         },
     )
 
 interface TogglePermissionAppListProvider {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index ec44d2a..bef2bda 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -149,13 +149,14 @@
 
     @Composable
     fun getSummary(record: T): () -> String {
+        val allowed = listModel.isAllowed(record)
         val restrictions =
             listModel.getRestrictions(
                 userId = record.app.userId,
                 packageName = record.app.packageName,
+                allowed()
             )
         val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions)
-        val allowed = listModel.isAllowed(record)
         return RestrictedSwitchPreferenceModel.getSummary(
             context = context,
             summaryIfNoRestricted = { getSummaryIfNoRestricted(allowed()) },
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml b/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml
index 834814c..1c6b1eb 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml
@@ -18,7 +18,8 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart">
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:filterTouchesWhenObscured="true">
 
     <com.android.settingslib.widget.CollapsableTextView
         android:id="@+id/collapsable_text_view"
diff --git a/packages/SettingsLib/res/drawable/dialog_btn_filled.xml b/packages/SettingsLib/res/drawable/dialog_btn_filled.xml
index 0614f9d..b4577d1 100644
--- a/packages/SettingsLib/res/drawable/dialog_btn_filled.xml
+++ b/packages/SettingsLib/res/drawable/dialog_btn_filled.xml
@@ -28,7 +28,7 @@
         <item>
             <shape android:shape="rectangle">
                 <corners android:radius="@dimen/button_corner_radius"/>
-                <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+                <solid android:color="?androidprv:attr/colorAccent"/>
                 <padding android:left="@dimen/dialog_button_horizontal_padding"
                          android:top="@dimen/dialog_button_vertical_padding"
                          android:right="@dimen/dialog_button_horizontal_padding"
diff --git a/packages/SettingsLib/res/drawable/dialog_btn_outline.xml b/packages/SettingsLib/res/drawable/dialog_btn_outline.xml
index a920b50..a15891c 100644
--- a/packages/SettingsLib/res/drawable/dialog_btn_outline.xml
+++ b/packages/SettingsLib/res/drawable/dialog_btn_outline.xml
@@ -29,7 +29,7 @@
             <shape android:shape="rectangle">
                 <corners android:radius="@dimen/button_corner_radius"/>
                 <solid android:color="@android:color/transparent"/>
-                <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant"
+                <stroke android:color="?androidprv:attr/colorAccent"
                         android:width="1dp"/>
                 <padding android:left="@dimen/dialog_button_horizontal_padding"
                          android:top="@dimen/dialog_button_vertical_padding"
diff --git a/packages/SettingsLib/res/layout/dialog_with_icon.xml b/packages/SettingsLib/res/layout/dialog_with_icon.xml
index b21895b..1c54291 100644
--- a/packages/SettingsLib/res/layout/dialog_with_icon.xml
+++ b/packages/SettingsLib/res/layout/dialog_with_icon.xml
@@ -16,6 +16,7 @@
 
 <ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:id="@+id/new_user_dialog_id">
@@ -30,6 +31,7 @@
             android:id="@+id/dialog_with_icon_icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:tint="?androidprv:attr/colorAccent"
             android:importantForAccessibility="no"/>
         <TextView
             android:id="@+id/dialog_with_icon_title"
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index ae95613..bb38f8f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -301,10 +301,10 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string>
-    <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de AVRCP del Bluetooth"</string>
-    <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión de AVRCP del Bluetooth"</string>
-    <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string>
-    <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Seleccionar versión de MAP de Bluetooth"</string>
+    <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión AVRCP del Bluetooth"</string>
+    <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión AVRCP del Bluetooth"</string>
+    <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión MAP del Bluetooth"</string>
+    <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Seleccionar versión MAP del Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Códec del audio Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Activar el códec de audio por Bluetooth\nSelección"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Frecuencia de muestreo del audio Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 9ed48b1..0628ba3 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -424,7 +424,7 @@
     <string name="transition_animation_scale_title" msgid="1278477690695439337">"Ülemineku animatsioonimastaap"</string>
     <string name="animator_duration_scale_title" msgid="7082913931326085176">"Animaatori kestuse mastaap"</string>
     <string name="overlay_display_devices_title" msgid="5411894622334469607">"Modelleeri teisi ekraane"</string>
-    <string name="shade_display_awareness_title" msgid="8000009404669495876">"Menüü kuvamise asukoht"</string>
+    <string name="shade_display_awareness_title" msgid="8000009404669495876">"Varju kuvamise asukoht"</string>
     <string name="debug_applications_category" msgid="5394089406638954196">"Rakendused"</string>
     <string name="immediately_destroy_activities" msgid="1826287490705167403">"Ära hoia tegevusi alles"</string>
     <string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Hävita kõik tegevused kohe, kui kasutaja neist lahkub"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 24dafa9..b257dd1 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -569,7 +569,7 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmak eta gogorarazpenak"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Eman alarmak ezartzeko eta denbora-muga duten ekintzak programatzeko baimena aplikazioari. Hala, aplikazioak atzeko planoan funtzionatuko du, eta litekeena da bateria gehiago kontsumitzea.\n\nBaimen hori ematen ez baduzu, ez dute funtzionatuko aplikazio honen bidez programatutako alarmek eta denbora-muga duten ekintzek."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programazioa, alarma, gogorarazpena, erlojua"</string>
-    <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Ez molestatzeko"</string>
+    <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Ez molestatzeko modua"</string>
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ez molestatzeko modua"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktibatu"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktibatu ez molestatzeko modua"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index bfe68fe..9b8f627 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -672,16 +672,16 @@
     <string name="grant_admin" msgid="4323199171790522574">"હા, તેમને ઍડમિન બનાવો"</string>
     <string name="not_grant_admin" msgid="3557849576157702485">"ના, તેમને ઍડમિન બનાવશો નહીં"</string>
     <string name="guest_exit_dialog_button" msgid="1736401897067442044">"બહાર નીકળો"</string>
-    <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"શું અતિથિ પ્રવૃત્તિ સાચવીએ?"</string>
-    <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"તમે હાલના સત્રની પ્રવૃત્તિ સાચવી શકો છો અથવા તમામ ઍપ અને ડેટા ડિલીટ કરી શકો છો"</string>
+    <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"શું અતિથિની ઍક્ટિવિટીને સાચવીએ?"</string>
+    <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"તમે હાલના સત્રની ઍક્ટિવિટીને સાચવી શકો છો અથવા તમામ ઍપ અને ડેટા ડિલીટ કરી શકો છો"</string>
     <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ડિલીટ કરો"</string>
     <string name="guest_exit_save_data_button" msgid="3690974510644963547">"સાચવો"</string>
     <string name="guest_exit_button" msgid="5774985819191803960">"અતિથિ મોડમાંથી બહાર નીકળો"</string>
     <string name="guest_reset_button" msgid="2515069346223503479">"અતિથિ સત્ર રીસેટ કરો"</string>
     <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"અતિથિ મોડમાંથી બહાર નીકળો"</string>
     <string name="guest_notification_ephemeral" msgid="7263252466950923871">"બહાર નીકળતી વખતે તમામ ઍક્ટિવિટી ડિલીટ કરવામાં આવશે"</string>
-    <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"બહાર નીકળતી વખતે તમે પ્રવૃત્તિ સાચવી કે ડિલીટ કરી શકશો"</string>
-    <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"સત્રની પ્રવૃત્તિ હમણાં ડિલીટ કરવા માટે રીસેટ કરો અથવા બહાર નીકળતી વખતે તમે પ્રવૃત્તિ સાચવી કે ડિલીટ કરી શકશો"</string>
+    <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"બહાર નીકળતી વખતે તમે ઍક્ટિવિટીને સાચવી કે ડિલીટ કરી શકશો"</string>
+    <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"સત્રની ઍક્ટિવિટીને હમણાં ડિલીટ કરવા માટે, રીસેટ કરો. અથવા બહાર નીકળતી વખતે તમે ઍક્ટિવિટીને સાચવી કે ડિલીટ કરી શકશો"</string>
     <string name="user_image_photo_selector" msgid="433658323306627093">"ફોટો પસંદ કરો"</string>
     <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"ઘણા બધા ખોટા પ્રયત્નો. આ ડિવાઇસનો ડેટા ડિલીટ કરવામાં આવશે."</string>
     <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"ઘણા બધા ખોટા પ્રયત્નો. આ વપરાશકર્તાને ડિલીટ કરવામાં આવશે."</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 5146ebc..1923a72e 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -250,7 +250,7 @@
     <string name="enable_adb" msgid="8072776357237289039">"यूएसबी डीबग करना"</string>
     <string name="enable_adb_summary" msgid="3711526030096574316">"डीबग मोड जब यूएसबी कनेक्‍ट किया गया हो"</string>
     <string name="clear_adb_keys" msgid="3010148733140369917">"यूएसबी डीबग करने की अनुमति रद्द करें"</string>
-    <string name="enable_adb_wireless" msgid="6973226350963971018">"वॉयरलेस डीबगिंग"</string>
+    <string name="enable_adb_wireless" msgid="6973226350963971018">"वायरलेस डीबगिंग"</string>
     <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"डिवाइस के वाई-फ़ाई से कनेक्ट हाेने पर, डीबग मोड चालू करें"</string>
     <string name="adb_wireless_error" msgid="721958772149779856">"गड़बड़ी"</string>
     <string name="adb_wireless_settings" msgid="2295017847215680229">"वॉयरलेस डीबगिंग"</string>
@@ -665,8 +665,8 @@
     <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रीसेट करें"</string>
     <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"हटाएं"</string>
     <string name="guest_resetting" msgid="7822120170191509566">"मेहमान के तौर पर ब्राउज़ करने का सेशन रीसेट किया जा रहा है…"</string>
-    <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"क्या मेहमान मोड के मौजूदा सेशन को रीसेट करना है?"</string>
-    <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"इससे मेहमान के तौर पर ब्राउज़ करने का नया सेशन शुरू हो जाएगा. इसके अलावा, इस्तेमाल किए जा रहे ऐप्लिकेशन पर की गई गतिविधि और मौजूदा सेशन का डेटा मिटा दिया जाएगा"</string>
+    <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"क्या मेहमान उपयोगकर्ता के मौजूदा सेशन को रीसेट करना है?"</string>
+    <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"इससे मेहमान के तौर पर ब्राउज़ करने का नया सेशन शुरू हो जाएगा. इसके अलावा, मौजूदा सेशन में डाउनलोड किए गए ऐप्लिकेशन और सेव किया गया सारा डेटा मिट जाएगा"</string>
     <string name="guest_exit_dialog_title" msgid="1846494656849381804">"मेहमान मोड से बाहर निकलना है?"</string>
     <string name="guest_exit_dialog_message" msgid="1743218864242719783">"इससे, मेहमान मोड के मौजूदा सेशन का डेटा और इसमें इस्तेमाल हो रहे ऐप्लिकेशन मिट जाएंगे"</string>
     <string name="grant_admin" msgid="4323199171790522574">"हां, इन्हें एडमिन बनाएं"</string>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
index d1771fc..5c1845f 100644
--- a/packages/SettingsLib/res/values-iw/arrays.xml
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -296,7 +296,7 @@
   <string-array name="shade_display_awareness_summaries">
     <item msgid="2964753205732912921">"הצגת הצללה במסך המכשיר בלבד"</item>
     <item msgid="7795034287069726554">"הצגת המכשיר במסך חיצוני אחד"</item>
-    <item msgid="5280431949814340475">"הצגת המכשיר במסך האחרון שהתמקדת בו"</item>
+    <item msgid="5280431949814340475">"הצגת ההתראות במסך האחרון שהשתמשת בו"</item>
   </string-array>
   <string-array name="shade_display_awareness_values">
     <item msgid="3055776101992426514">"default_display"</item>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index bf039ad..be26f71 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -424,7 +424,7 @@
     <string name="transition_animation_scale_title" msgid="1278477690695439337">"קנה מידה לאנימציית מעבר"</string>
     <string name="animator_duration_scale_title" msgid="7082913931326085176">"קנה מידה למשך זמן אנימציה"</string>
     <string name="overlay_display_devices_title" msgid="5411894622334469607">"יצירת הדמיה של תצוגות משניות"</string>
-    <string name="shade_display_awareness_title" msgid="8000009404669495876">"מיקום התצוגה של ההצללה"</string>
+    <string name="shade_display_awareness_title" msgid="8000009404669495876">"מיקום לוח ההתראות"</string>
     <string name="debug_applications_category" msgid="5394089406638954196">"אפליקציות"</string>
     <string name="immediately_destroy_activities" msgid="1826287490705167403">"ללא שמירת פעילויות"</string>
     <string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"השמדת כל פעילות ברגע שהמשתמש עוזב אותה"</string>
@@ -681,7 +681,7 @@
     <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"יציאה ממצב אורח"</string>
     <string name="guest_notification_ephemeral" msgid="7263252466950923871">"כל הפעילות תימחק ביציאה"</string>
     <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"אפשר לשמור או למחוק את הפעילות שלך ביציאה"</string>
-    <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ניתן לאפס כדי למחוק את הפעילות מהסשן כעת, או לשמור או למחוק את הפעילות ביציאה"</string>
+    <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"אפשר לאפס כדי למחוק עכשיו את הפעילות מהסשן, או לשמור או למחוק את הפעילות ביציאה"</string>
     <string name="user_image_photo_selector" msgid="433658323306627093">"בחירת תמונה"</string>
     <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"נעשו יותר מדי ניסיונות שגויים. הנתונים במכשיר יימחקו."</string>
     <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"נעשו יותר מדי ניסיונות שגויים. המשתמש הזה יימחק."</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 08dfa7d..a1ab4f3 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -690,7 +690,7 @@
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Төхөөрөмжийн өгөгдмөл"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Идэвхгүй болгосон"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string>
-    <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Энэ өөрчлөлтийг хэрэгжүүлэхийн тулд таны төхөөрөмжийг дахин асаах ёстой. Одоо дахин асаах эсвэл цуцлана уу."</string>
+    <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Энэ өөрчлөлтийг хэрэгжүүлэхийн тулд таны төхөөрөмжийг дахин асаах ёстой. Одоо дахин асаах эсвэл цуцална уу."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Утастай чихэвч"</string>
     <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Утастай аудио"</string>
     <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 3f88af6..1af9286 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -666,14 +666,14 @@
     <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"काढून टाका"</string>
     <string name="guest_resetting" msgid="7822120170191509566">"अतिथीला रीसेट करत आहे…"</string>
     <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"अतिथी सत्र रीसेट करायचे का?"</string>
-    <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"हे नवीन अतिथी सत्र सुरू करेल आणि सध्याच्या सत्रातील सर्व अ‍ॅप्स व डेटा हटवेल"</string>
+    <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"हे नवीन अतिथी सत्र सुरू करेल आणि सद्य सत्रातील सर्व अ‍ॅप्स व डेटा हटवेल"</string>
     <string name="guest_exit_dialog_title" msgid="1846494656849381804">"अतिथी मोडमधून बाहेर पडायचे का?"</string>
     <string name="guest_exit_dialog_message" msgid="1743218864242719783">"हे सध्याच्या अतिथी सत्रातील अ‍ॅप्स आणि डेटा हटवेल"</string>
     <string name="grant_admin" msgid="4323199171790522574">"होय, त्यांना ॲडमिन करा"</string>
     <string name="not_grant_admin" msgid="3557849576157702485">"नाही, त्यांना ॲडमिन करू नका"</string>
     <string name="guest_exit_dialog_button" msgid="1736401897067442044">"बाहेर पडा"</string>
     <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"अतिथी अ‍ॅक्टिव्हिटी सेव्ह करायची का?"</string>
-    <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"सध्याच्या सत्रातील अ‍ॅक्टिव्हिटी सेव्ह करू किंवा सर्व अ‍ॅप्स व डेटा हटवू शकता"</string>
+    <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"सद्य सत्रातील अ‍ॅक्टिव्हिटी सेव्ह करू शकता किंवा सर्व अ‍ॅप्स व डेटा हटवू शकता"</string>
     <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"हटवा"</string>
     <string name="guest_exit_save_data_button" msgid="3690974510644963547">"सेव्ह करा"</string>
     <string name="guest_exit_button" msgid="5774985819191803960">"अतिथी मोडमधून बाहेर पडा"</string>
@@ -727,7 +727,7 @@
     <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"इथरनेट डिस्कनेक्ट केले."</string>
     <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"इथरनेट."</string>
     <string name="accessibility_no_calling" msgid="3540827068323895748">"कॉलिंग उपलब्ध नाही."</string>
-    <string name="physical_keyboard_title" msgid="4811935435315835220">"वास्तविक कीबोर्ड"</string>
+    <string name="physical_keyboard_title" msgid="4811935435315835220">"प्रत्यक्ष कीबोर्ड"</string>
     <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"किबोर्ड लेआउट निवडा"</string>
     <string name="keyboard_layout_default_label" msgid="1997292217218546957">"डीफॉल्ट"</string>
     <string name="turn_screen_on_title" msgid="2662312432042116026">"स्क्रीन सुरू करण्यासंबंधित नियंत्रण"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 9525884..4a31a12 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -424,7 +424,7 @@
     <string name="transition_animation_scale_title" msgid="1278477690695439337">"သက်ဝင်အသွင်ပြောင်းခြင်း"</string>
     <string name="animator_duration_scale_title" msgid="7082913931326085176">"လှုပ်ရှားမှုကြာချိန်စကေး"</string>
     <string name="overlay_display_devices_title" msgid="5411894622334469607">"ဆင့်ပွားမျက်နှာပြင် အသွင်ဆောင်ခြင်း"</string>
-    <string name="shade_display_awareness_title" msgid="8000009404669495876">"အရိပ်ပြကွက် တည်နေရာ"</string>
+    <string name="shade_display_awareness_title" msgid="8000009404669495876">"အကြောင်းကြားချက်ပြကွက် နေရာ"</string>
     <string name="debug_applications_category" msgid="5394089406638954196">"အက်ပ်များ"</string>
     <string name="immediately_destroy_activities" msgid="1826287490705167403">"ဆောင်ရွက်မှုများကို သိမ်းမထားပါနှင့်"</string>
     <string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"သုံးသူထွက်သွားသည်နှင့် လုပ်ဆောင်ချက်များ ဖျက်ရန်"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 803309c..869d9a1 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -615,7 +615,7 @@
     <string name="help_label" msgid="3528360748637781274">"ସାହାଯ୍ୟ ଓ ମତାମତ"</string>
     <string name="storage_category" msgid="2287342585424631813">"ଷ୍ଟୋରେଜ"</string>
     <string name="shared_data_title" msgid="1017034836800864953">"ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା"</string>
-    <string name="shared_data_summary" msgid="5516326713822885652">"ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା ଦେଖନ୍ତୁ ଏବଂ ଏହାକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
+    <string name="shared_data_summary" msgid="5516326713822885652">"ସେୟାର ହୋଇଥିବା ଡାଟା ଭ୍ୟୁ କରନ୍ତୁ ଏବଂ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ କୌଣସି ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା ନାହିଁ।"</string>
     <string name="shared_data_query_failure_text" msgid="3489828881998773687">"ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା ଫେଚ୍ କରିବା ସମୟରେ ଏକ ତ୍ରୁଟି ହୋଇଥିଲା। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
     <string name="blob_id_text" msgid="8680078988996308061">"ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା ID: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index d8726db..6c0ce86 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -680,8 +680,8 @@
     <string name="guest_reset_button" msgid="2515069346223503479">"ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਨੂੰ ਰੀਸੈੱਟ ਕਰੋ"</string>
     <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ਮਹਿਮਾਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string>
     <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ਬਾਹਰ ਜਾਣ \'ਤੇ ਸਾਰੀ ਸਰਗਰਮੀ ਮਿਟਾ ਦਿੱਤੀ ਜਾਵੇਗੀ"</string>
-    <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ਤੁਸੀਂ ਬਾਹਰ ਜਾਣ \'ਤੇ ਆਪਣੀ ਸਰਗਰਮੀ ਰੱਖਿਅਤ ਕਰ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string>
-    <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ਸੈਸ਼ਨ ਦੀ ਸਰਗਰਮੀ ਹੁਣੇ ਮਿਟਾਉਣ ਲਈ ਰੀਸੈੱਟ ਕਰੋ ਜਾਂ ਤੁਸੀਂ ਬਾਹਰ ਜਾਣ \'ਤੇ ਸਰਗਰਮੀ ਨੂੰ ਰੱਖਿਅਤ ਕਰ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string>
+    <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ਤੁਸੀਂ ਬਾਹਰ ਜਾਣ ਸਮੇਂ ਆਪਣੀ ਸਰਗਰਮੀ ਰੱਖਿਅਤ ਕਰ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string>
+    <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ਸੈਸ਼ਨ ਦੀ ਸਰਗਰਮੀ ਹੁਣੇ ਮਿਟਾਉਣ ਲਈ ਰੀਸੈੱਟ ਕਰੋ ਜਾਂ ਤੁਸੀਂ ਬਾਹਰ ਜਾਣ ਸਮੇਂ ਸਰਗਰਮੀ ਨੂੰ ਰੱਖਿਅਤ ਕਰ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string>
     <string name="user_image_photo_selector" msgid="433658323306627093">"ਫ਼ੋਟੋ ਚੁਣੋ"</string>
     <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"ਬਹੁਤ ਸਾਰੀਆਂ ਗਲਤ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸ ਡੀਵਾਈਸ ਦਾ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
     <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"ਬਹੁਤ ਸਾਰੀਆਂ ਗਲਤ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸ ਵਰਤੋਂਕਾਰ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index 2f870bf..2829f20 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -296,7 +296,7 @@
   <string-array name="shade_display_awareness_summaries">
     <item msgid="2964753205732912921">"Mostrar a sombra apenas no ecrã do dispositivo"</item>
     <item msgid="7795034287069726554">"Mostrar o dispositivo num único ecrã externo"</item>
-    <item msgid="5280431949814340475">"Mostrar o dispositivo no último ecrã focado"</item>
+    <item msgid="5280431949814340475">"Apresente o dispositivo no último ecrã focado"</item>
   </string-array>
   <string-array name="shade_display_awareness_values">
     <item msgid="3055776101992426514">"default_display"</item>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 9d56b43..457aacd 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -251,7 +251,7 @@
     <string name="enable_adb_summary" msgid="3711526030096574316">"Modo de depuração com USB ligado"</string>
     <string name="clear_adb_keys" msgid="3010148733140369917">"Revogar autorizações de depur. USB"</string>
     <string name="enable_adb_wireless" msgid="6973226350963971018">"Depuração sem fios"</string>
-    <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Modo de depuração quando o Wi-Fi está ligado"</string>
+    <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Modo de depuração com Wi-Fi ligado"</string>
     <string name="adb_wireless_error" msgid="721958772149779856">"Erro."</string>
     <string name="adb_wireless_settings" msgid="2295017847215680229">"Depuração sem fios"</string>
     <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Para ver e utilizar dispositivos disponíveis, ative a depuração sem fios."</string>
@@ -279,7 +279,7 @@
     <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Estabeleça ligação a uma rede Wi-Fi."</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, depurar, programador"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"Atalho para relatório de erro"</string>
-    <string name="bugreport_in_power_summary" msgid="1885529649381831775">"Mostrar um botão no menu ligar/desligar para criar um relatório de erro"</string>
+    <string name="bugreport_in_power_summary" msgid="1885529649381831775">"Acrescente um botão ao menu ligar/desligar para criar um relatório de erro"</string>
     <string name="keep_screen_on" msgid="1187161672348797558">"Manter ativo"</string>
     <string name="keep_screen_on_summary" msgid="1510731514101925829">"O ecrã nunca entrará em suspensão durante o carregamento"</string>
     <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Ativar registo de monitorização Bluetooth HCI"</string>
@@ -288,8 +288,8 @@
     <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Permitir o desbloqueio do carregador de arranque"</string>
     <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Permitir o desbloqueio de OEM?"</string>
     <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"AVISO: as funcionalidades de proteção do dispositivo não funcionam neste dispositivo enquanto esta definição estiver ativada."</string>
-    <string name="mock_location_app" msgid="6269380172542248304">"Selecionar app de localização fictícia"</string>
-    <string name="mock_location_app_not_set" msgid="6972032787262831155">"Aplicação de localização fictícia não definida"</string>
+    <string name="mock_location_app" msgid="6269380172542248304">"Selecionar app de localização de teste"</string>
+    <string name="mock_location_app_not_set" msgid="6972032787262831155">"App de localização de teste não definida"</string>
     <string name="mock_location_app_set" msgid="4706722469342913843">"Aplicação de localização fictícia: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="debug_networking_category" msgid="6829757985772659599">"Redes"</string>
     <string name="wifi_display_certification" msgid="1805579519992520381">"Certificação de display sem fios"</string>
@@ -359,7 +359,7 @@
     <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"Ativar aplicação terminal que oferece acesso local à shell"</string>
     <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Ambiente de programação Linux"</string>
-    <string name="enable_linux_terminal_summary" msgid="2029479880888108902">"(Experimental) Executar terminal do Linux no Android"</string>
+    <string name="enable_linux_terminal_summary" msgid="2029479880888108902">"(Experimental) Execute o terminal do Linux no Android"</string>
     <string name="disable_linux_terminal_disclaimer" msgid="3054320531778388231">"Se desativar a app, os dados do terminal do Linux são limpos"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"Verificação HDCP"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Definir o comportamento da verificação HDCP"</string>
@@ -412,19 +412,19 @@
     <string name="track_frame_time" msgid="522674651937771106">"Renderização HWUI do perfil"</string>
     <string name="enable_gpu_debug_layers" msgid="4986675516188740397">"Ativar camadas de depuração GPU"</string>
     <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite carregamento de camadas de depuração de GPU para apps de depuração"</string>
-    <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Ativ. registo do fornecedor"</string>
-    <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Inclua registos adicionais de fornecedores específicos de dispositivos em relatórios de erros, que podem conter informações privadas, utilizar mais bateria e/ou utilizar mais armazenamento."</string>
+    <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Ativar registo verboso de fornecedor"</string>
+    <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Inclua registos adicionais de fornecedores específicos de dispositivos em relatórios de erro, que podem conter informações privadas, usar mais bateria e/ou mais armazenamento."</string>
     <string name="enable_verbose_vendor_logging_checkbox" msgid="3864578373293835530">"Desativar após um dia"</string>
     <string name="verbose_vendor_logging_notification_title" msgid="6811217272559843592">"O registo verboso de fornecedor terminou"</string>
     <string name="verbose_vendor_logging_notification_summary" msgid="5226524769774370942">"Ativado durante um dia"</string>
     <string name="verbose_vendor_logging_notification_action" msgid="1190831050259046071">"Ativar durante mais um dia"</string>
     <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"É desativado após um dia"</string>
     <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"Ativado indefinidamente"</string>
-    <string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animação de transição"</string>
+    <string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animação de janelas"</string>
     <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
     <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração de animação"</string>
-    <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular apresentações secundárias"</string>
-    <string name="shade_display_awareness_title" msgid="8000009404669495876">"Posição da sombra no ecrã"</string>
+    <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular ecrãs secundários"</string>
+    <string name="shade_display_awareness_title" msgid="8000009404669495876">"Posição do painel no ecrã"</string>
     <string name="debug_applications_category" msgid="5394089406638954196">"Apps"</string>
     <string name="immediately_destroy_activities" msgid="1826287490705167403">"Não manter atividades"</string>
     <string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Destruir atividades assim que o utilizador sair"</string>
@@ -615,7 +615,7 @@
     <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
     <string name="storage_category" msgid="2287342585424631813">"Armazenamento"</string>
     <string name="shared_data_title" msgid="1017034836800864953">"Dados partilhados"</string>
-    <string name="shared_data_summary" msgid="5516326713822885652">"Ver e modificar dados partilhados"</string>
+    <string name="shared_data_summary" msgid="5516326713822885652">"Veja e modifique dados partilhados"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Não existem dados partilhados para este utilizador."</string>
     <string name="shared_data_query_failure_text" msgid="3489828881998773687">"Ocorreu um erro ao obter os dados partilhados. Tente novamente."</string>
     <string name="blob_id_text" msgid="8680078988996308061">"ID de dados partilhados: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index c66242b..49d520e 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -288,9 +288,9 @@
     <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Tillåt att bootloadern låses upp"</string>
     <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Vill du tillåta OEM-upplåsning?"</string>
     <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"Varning! Funktionerna för enhetsskydd fungerar inte på enheten när den här inställningen är aktiverad."</string>
-    <string name="mock_location_app" msgid="6269380172542248304">"Välj app för påhittad plats"</string>
-    <string name="mock_location_app_not_set" msgid="6972032787262831155">"Ingen app för påhittad plats har angetts"</string>
-    <string name="mock_location_app_set" msgid="4706722469342913843">"App för påhittad plats: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="mock_location_app" msgid="6269380172542248304">"Välj app för simulerad plats"</string>
+    <string name="mock_location_app_not_set" msgid="6972032787262831155">"Ingen app för simulerad plats har angetts"</string>
+    <string name="mock_location_app_set" msgid="4706722469342913843">"App för simulerad plats: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="debug_networking_category" msgid="6829757985772659599">"Nätverk"</string>
     <string name="wifi_display_certification" msgid="1805579519992520381">"Certifiering för wifi-skärmdelning"</string>
     <string name="wifi_verbose_logging" msgid="1785910450009679371">"Aktivera utförlig loggning för wifi"</string>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index c18d771..8dea6ed 100644
--- a/packages/SettingsLib/res/values-te/arrays.xml
+++ b/packages/SettingsLib/res/values-te/arrays.xml
@@ -296,7 +296,7 @@
   <string-array name="shade_display_awareness_summaries">
     <item msgid="2964753205732912921">"షేడ్‌ను పరికర డిస్‌ప్లేలో మాత్రమే చూపండి"</item>
     <item msgid="7795034287069726554">"పరికరాన్ని ఒక ఎక్స్‌టర్నల్ డిస్‌ప్లేలో మాత్రమే చూపండి"</item>
-    <item msgid="5280431949814340475">"పరికరాన్ని ఫోకస్ చేసిన చివరి డిస్‌ప్లేలో మాత్రమే చూపండి"</item>
+    <item msgid="5280431949814340475">"పరికరాన్ని చివరగా యాక్టివ్‌గా ఉన్న స్క్రీన్‌లో చూపండి"</item>
   </string-array>
   <string-array name="shade_display_awareness_values">
     <item msgid="3055776101992426514">"default_display"</item>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index aae20b2..d0b7634 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -358,7 +358,7 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"మెరుగైన కనెక్టివిటీ ఫీచర్‌ను ఎనేబుల్ చేస్తుంది."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"స్థానిక టెర్మినల్"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ యాక్సెస్‌ను అందించే టెర్మినల్ యాప్‌ను ప్రారంభించండి"</string>
-    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux డెవలప్మెంట్ ఎన్విరాన్మెంట్"</string>
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux డెవలప్‌మెంట్ ఎన్విరాన్‌మెంట్"</string>
     <string name="enable_linux_terminal_summary" msgid="2029479880888108902">"(ప్రయోగాత్మకం) Linux టెర్మినల్‌ను Androidలో రన్ చేయండి"</string>
     <string name="disable_linux_terminal_disclaimer" msgid="3054320531778388231">"మీరు డిజేబుల్ చేస్తే, Linux టెర్మినల్ డేటా క్లియర్ అవుతుంది"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP చెకింగ్‌"</string>
diff --git a/packages/SettingsLib/res/values/styles.xml b/packages/SettingsLib/res/values/styles.xml
index 1249c6b..3326b60 100644
--- a/packages/SettingsLib/res/values/styles.xml
+++ b/packages/SettingsLib/res/values/styles.xml
@@ -94,7 +94,7 @@
 
     <style name="DialogButtonPositive">
         <item name="android:background">@drawable/dialog_btn_filled</item>
-        <item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
         <item name="android:textSize">14sp</item>
         <item name="android:lineHeight">20sp</item>
         <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
index c36ade9..d3219c2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
@@ -41,10 +41,23 @@
      * package. Marks the preference as disabled if so.
      * @param settingIdentifier The key identifying the setting
      * @param packageName the package to check the settingIdentifier for
+     * @param settingEnabled Whether the setting in question is enabled
+     */
+    public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+            @NonNull String packageName, boolean settingEnabled) {
+        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, settingEnabled);
+    }
+
+    /**
+     * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+     * package. Marks the preference as disabled if so.
+     * TODO b/390196024: remove this and update all callers to use the "settingEnabled" version
+     * @param settingIdentifier The key identifying the setting
+     * @param packageName the package to check the settingIdentifier for
      */
     public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
             @NonNull String packageName) {
-        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, false);
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 332042a..ec1b2b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -107,12 +107,25 @@
     /**
      * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
      * package. Marks the preference as disabled if so.
+     * TODO b/390196024: remove this and update all callers to use the "settingEnabled" version
      * @param settingIdentifier The key identifying the setting
      * @param packageName the package to check the settingIdentifier for
      */
     public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
             @NonNull String packageName) {
-        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, false);
+    }
+
+    /**
+     * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+     * package. Marks the preference as disabled if so.
+     * @param settingIdentifier The key identifying the setting
+     * @param packageName the package to check the settingIdentifier for
+     * @param settingEnabled Whether the setting in question is enabled
+     */
+    public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+            @NonNull String packageName, boolean settingEnabled) {
+        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, settingEnabled);
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 25628fb..212e43a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -204,16 +204,33 @@
      * package. Marks the preference as disabled if so.
      * @param settingIdentifier The key identifying the setting
      * @param packageName the package to check the settingIdentifier for
+     * @param settingEnabled Whether the setting in question is enabled
      */
     public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
-            @NonNull String packageName) {
+            @NonNull String packageName, boolean settingEnabled) {
         updatePackageDetails(packageName, android.os.Process.INVALID_UID);
+        if (settingEnabled) {
+            setDisabledByEcm(null);
+            return;
+        }
         Intent intent = RestrictedLockUtilsInternal.checkIfRequiresEnhancedConfirmation(
                 mContext, settingIdentifier, packageName);
         setDisabledByEcm(intent);
     }
 
     /**
+     * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+     * package. Marks the preference as disabled if so.
+     * TODO b/390196024: remove this and update all callers to use the "settingEnabled" version
+     * @param settingIdentifier The key identifying the setting
+     * @param packageName the package to check the settingIdentifier for
+     */
+    public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+            @NonNull String packageName) {
+        checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, false);
+    }
+
+    /**
      * @return EnforcedAdmin if we have been passed the restriction in the xml.
      */
     public EnforcedAdmin checkRestrictionEnforced() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
index 573869d..f8f16a9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
@@ -134,10 +134,11 @@
      *
      * @param settingIdentifier The key identifying the setting
      * @param packageName the package to check the settingIdentifier for
+     * @param settingEnabled Whether the setting in question is enabled
      */
-    public void checkEcmRestrictionAndSetDisabled(
-            @NonNull String settingIdentifier, @NonNull String packageName) {
-        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+    public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+            @NonNull String packageName, boolean settingEnabled) {
+        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, settingEnabled);
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 0aac9a11..a5fa6a8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -220,10 +220,23 @@
      * package. Marks the preference as disabled if so.
      * @param settingIdentifier The key identifying the setting
      * @param packageName the package to check the settingIdentifier for
+     * @param settingEnabled Whether the setting in question is enabled
      */
     public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
-            @NonNull  String packageName) {
-        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+            @NonNull String packageName, boolean settingEnabled) {
+        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, settingEnabled);
+    }
+
+    /**
+     * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+     * package. Marks the preference as disabled if so.
+     * TODO b/390196024: remove this and update all callers to use the "settingEnabled" version
+     * @param settingIdentifier The key identifying the setting
+     * @param packageName the package to check the settingIdentifier for
+     */
+    public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+            @NonNull String packageName) {
+        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, false);
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 68e9fe7..a00484a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1169,4 +1169,38 @@
         String metadataValue = getFastPairCustomizedField(bluetoothDevice, TEMP_BOND_TYPE);
         return Objects.equals(metadataValue, TEMP_BOND_DEVICE_METADATA_VALUE);
     }
+
+    /**
+     * Set temp bond metadata to device
+     *
+     * @param device the BluetoothDevice to be marked as temp bond
+     *
+     * Note: It is a workaround since Bluetooth API is not ready.
+     *       Avoid using this method if possible
+     */
+    public static void setTemporaryBondMetadata(@Nullable BluetoothDevice device) {
+        if (device == null) return;
+        if (!Flags.enableTemporaryBondDevicesUi()) {
+            Log.d(TAG, "Skip setTemporaryBondMetadata, flag is disabled");
+            return;
+        }
+        String fastPairCustomizedMeta = getStringMetaData(device,
+                METADATA_FAST_PAIR_CUSTOMIZED_FIELDS);
+        String fullContentWithTag = generateExpressionWithTag(TEMP_BOND_TYPE,
+                TEMP_BOND_DEVICE_METADATA_VALUE);
+        if (TextUtils.isEmpty(fastPairCustomizedMeta)) {
+            fastPairCustomizedMeta = fullContentWithTag;
+        } else {
+            String oldValue = extraTagValue(TEMP_BOND_TYPE, fastPairCustomizedMeta);
+            if (TextUtils.isEmpty(oldValue)) {
+                fastPairCustomizedMeta += fullContentWithTag;
+            } else {
+                fastPairCustomizedMeta =
+                        fastPairCustomizedMeta.replace(
+                                generateExpressionWithTag(TEMP_BOND_TYPE, oldValue),
+                                fullContentWithTag);
+            }
+        }
+        device.setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, fastPairCustomizedMeta.getBytes());
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
index e3d7902..0097381 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
@@ -84,7 +84,11 @@
             return false;
         }
         DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
-        return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm,
+        final SupervisionManager sm =
+                android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()
+                        ? context.getSystemService(SupervisionManager.class)
+                        : null;
+        return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm, sm,
                 BiometricAuthenticator.TYPE_ANY_BIOMETRIC, new UserHandle(UserHandle.myUserId()));
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index ad196b8..4ee9ff0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -658,12 +658,9 @@
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
             RouteListingPreference routeListingPreference = getRouteListingPreference();
             if (routeListingPreference != null) {
-                final List<RouteListingPreference.Item> preferenceRouteListing =
-                        Api34Impl.composePreferenceRouteListing(
-                                routeListingPreference);
                 availableRoutes = Api34Impl.arrangeRouteListByPreference(selectedRoutes,
                         getAvailableRoutesFromRouter(),
-                                preferenceRouteListing);
+                        routeListingPreference);
             }
             return Api34Impl.filterDuplicatedIds(availableRoutes);
         } else {
@@ -760,11 +757,15 @@
         @DoNotInline
         static List<RouteListingPreference.Item> composePreferenceRouteListing(
                 RouteListingPreference routeListingPreference) {
+            boolean preferRouteListingOrdering =
+                    com.android.media.flags.Flags.enableOutputSwitcherSessionGrouping()
+                    && preferRouteListingOrdering(routeListingPreference);
             List<RouteListingPreference.Item> finalizedItemList = new ArrayList<>();
             List<RouteListingPreference.Item> itemList = routeListingPreference.getItems();
             for (RouteListingPreference.Item item : itemList) {
                 // Put suggested devices on the top first before further organization
-                if ((item.getFlags() & RouteListingPreference.Item.FLAG_SUGGESTED) != 0) {
+                if (!preferRouteListingOrdering
+                        && (item.getFlags() & RouteListingPreference.Item.FLAG_SUGGESTED) != 0) {
                     finalizedItemList.add(0, item);
                 } else {
                     finalizedItemList.add(item);
@@ -792,7 +793,7 @@
          * Returns an ordered list of available devices based on the provided {@code
          * routeListingPreferenceItems}.
          *
-         * <p>The result has the following order:
+         * <p>The resulting order if enableOutputSwitcherSessionGrouping is disabled is:
          *
          * <ol>
          *   <li>Selected routes.
@@ -800,22 +801,54 @@
          *   <li>Not-selected, non-system, available routes sorted by route listing preference.
          * </ol>
          *
+         * <p>The resulting order if enableOutputSwitcherSessionGrouping is enabled is:
+         *
+         * <ol>
+         *   <li>Selected routes sorted by route listing preference.
+         *   <li>Selected routes not defined by route listing preference.
+         *   <li>Not-selected system routes.
+         *   <li>Not-selected, non-system, available routes sorted by route listing preference.
+         * </ol>
+         *
+         *
          * @param selectedRoutes List of currently selected routes.
          * @param availableRoutes List of available routes that match the app's requested route
          *     features.
-         * @param routeListingPreferenceItems Ordered list of {@link RouteListingPreference.Item} to
-         *     sort routes with.
+         * @param routeListingPreference Preferences provided by the app to determine route order.
          */
         @DoNotInline
         static List<MediaRoute2Info> arrangeRouteListByPreference(
                 List<MediaRoute2Info> selectedRoutes,
                 List<MediaRoute2Info> availableRoutes,
-                List<RouteListingPreference.Item> routeListingPreferenceItems) {
+                RouteListingPreference routeListingPreference) {
+            final List<RouteListingPreference.Item> routeListingPreferenceItems =
+                    Api34Impl.composePreferenceRouteListing(routeListingPreference);
+
             Set<String> sortedRouteIds = new LinkedHashSet<>();
 
+            boolean addSelectedRlpItemsFirst =
+                    com.android.media.flags.Flags.enableOutputSwitcherSessionGrouping()
+                    && preferRouteListingOrdering(routeListingPreference);
+            Set<String> selectedRouteIds = new HashSet<>();
+
+            if (addSelectedRlpItemsFirst) {
+                // Add selected RLP items first
+                for (MediaRoute2Info selectedRoute : selectedRoutes) {
+                    selectedRouteIds.add(selectedRoute.getId());
+                }
+                for (RouteListingPreference.Item item: routeListingPreferenceItems) {
+                    if (selectedRouteIds.contains(item.getRouteId())) {
+                        sortedRouteIds.add(item.getRouteId());
+                    }
+                }
+            }
+
             // Add selected routes first.
-            for (MediaRoute2Info selectedRoute : selectedRoutes) {
-                sortedRouteIds.add(selectedRoute.getId());
+            if (com.android.media.flags.Flags.enableOutputSwitcherSessionGrouping()
+                    && sortedRouteIds.size() != selectedRoutes.size()) {
+                for (MediaRoute2Info selectedRoute : selectedRoutes) {
+                    sortedRouteIds.add(selectedRoute.getId());
+                }
             }
 
             // Add not-yet-added system routes.
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java b/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
index 4cf3bc2..6e64c59 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
@@ -173,6 +173,11 @@
      * //TODO: modify method to allow setting state for any button.
      */
     public CustomDialogHelper setButtonEnabled(boolean enabled) {
+        if (enabled) {
+            mPositiveButton.setAlpha(1f);
+        } else {
+            mPositiveButton.setAlpha(0.5f);
+        }
         mPositiveButton.setEnabled(enabled);
         return this;
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index cafe19f..7c46db9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -22,9 +22,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 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;
@@ -44,6 +46,8 @@
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 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.provider.Settings;
 import android.util.Pair;
@@ -109,6 +113,7 @@
                     + "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>";
     private static final String TEMP_BOND_METADATA =
             "<TEMP_BOND_TYPE>" + LE_AUDIO_SHARING_METADATA + "</TEMP_BOND_TYPE>";
+    private static final String FAKE_TEMP_BOND_METADATA = "<TEMP_BOND_TYPE>fake</TEMP_BOND_TYPE>";
     private static final String TEST_EXCLUSIVE_MANAGER_PACKAGE = "com.test.manager";
     private static final String TEST_EXCLUSIVE_MANAGER_COMPONENT = "com.test.manager/.component";
     private static final int TEST_BROADCAST_ID = 25;
@@ -1348,4 +1353,34 @@
 
         assertThat(BluetoothUtils.isTemporaryBondDevice(mBluetoothDevice)).isTrue();
     }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
+    public void setTemporaryBondDevice_flagOff_doNothing() {
+        when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+                .thenReturn(new byte[]{});
+        BluetoothUtils.setTemporaryBondMetadata(mBluetoothDevice);
+        verify(mBluetoothDevice, never()).setMetadata(eq(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS),
+                any());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
+    public void setTemporaryBondDevice_flagOn_setCorrectValue() {
+        when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+                .thenReturn(new byte[]{});
+        BluetoothUtils.setTemporaryBondMetadata(mBluetoothDevice);
+        verify(mBluetoothDevice).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
+                TEMP_BOND_METADATA.getBytes());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
+    public void setTemporaryBondDevice_flagOff_replaceAndSetCorrectValue() {
+        when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+                .thenReturn(FAKE_TEMP_BOND_METADATA.getBytes());
+        BluetoothUtils.setTemporaryBondMetadata(mBluetoothDevice);
+        verify(mBluetoothDevice).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
+                TEMP_BOND_METADATA.getBytes());
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index e1447dc..1a83f0a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -48,15 +48,20 @@
 import android.media.RoutingSessionInfo;
 import android.media.session.MediaSessionManager;
 import android.os.Build;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
+import com.android.media.flags.Flags;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.media.InfoMediaManager.Api34Impl;
 import com.android.settingslib.testutils.shadow.ShadowRouter2Manager;
 
 import com.google.common.collect.ImmutableList;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -122,6 +127,8 @@
                     .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
                     .build();
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Mock
     private MediaRouter2Manager mRouterManager;
     @Mock
@@ -377,21 +384,26 @@
     }
 
     private RouteListingPreference setUpPreferenceList(String packageName) {
+        return setUpPreferenceList(packageName, false);
+    }
+
+    private RouteListingPreference setUpPreferenceList(
+                String packageName, boolean useSystemOrdering) {
         ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
                 Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
         final List<RouteListingPreference.Item> preferenceItemList = new ArrayList<>();
-        RouteListingPreference.Item item1 =
+        RouteListingPreference.Item item1 = new RouteListingPreference.Item.Builder(
+                TEST_ID_3).build();
+        RouteListingPreference.Item item2 =
                 new RouteListingPreference.Item.Builder(TEST_ID_4)
                         .setFlags(RouteListingPreference.Item.FLAG_SUGGESTED)
                         .build();
-        RouteListingPreference.Item item2 = new RouteListingPreference.Item.Builder(
-                TEST_ID_3).build();
         preferenceItemList.add(item1);
         preferenceItemList.add(item2);
 
         RouteListingPreference routeListingPreference =
                 new RouteListingPreference.Builder().setItems(
-                        preferenceItemList).setUseSystemOrdering(false).build();
+                        preferenceItemList).setUseSystemOrdering(useSystemOrdering).build();
         when(mRouterManager.getRouteListingPreference(packageName))
                 .thenReturn(routeListingPreference);
         return routeListingPreference;
@@ -908,4 +920,66 @@
         assertThat(device.getState()).isEqualTo(STATE_SELECTED);
         assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(device);
     }
+
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void composePreferenceRouteListing_useSystemOrderingIsFalse() {
+        RouteListingPreference routeListingPreference =
+                setUpPreferenceList(TEST_PACKAGE_NAME, false);
+
+        List<RouteListingPreference.Item> routeOrder =
+                Api34Impl.composePreferenceRouteListing(routeListingPreference);
+
+        assertThat(routeOrder.get(0).getRouteId()).isEqualTo(TEST_ID_3);
+        assertThat(routeOrder.get(1).getRouteId()).isEqualTo(TEST_ID_4);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void composePreferenceRouteListing_useSystemOrderingIsTrue() {
+        RouteListingPreference routeListingPreference =
+                setUpPreferenceList(TEST_PACKAGE_NAME, true);
+
+        List<RouteListingPreference.Item> routeOrder =
+                Api34Impl.composePreferenceRouteListing(routeListingPreference);
+
+        assertThat(routeOrder.get(0).getRouteId()).isEqualTo(TEST_ID_4);
+        assertThat(routeOrder.get(1).getRouteId()).isEqualTo(TEST_ID_3);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void arrangeRouteListByPreference_useSystemOrderingIsFalse() {
+        RouteListingPreference routeListingPreference =
+                setUpPreferenceList(TEST_PACKAGE_NAME, false);
+        List<MediaRoute2Info> routes = setAvailableRoutesList(TEST_PACKAGE_NAME);
+        when(mRouterManager.getSelectedRoutes(any())).thenReturn(routes);
+
+        List<MediaRoute2Info> routeOrder =
+                Api34Impl.arrangeRouteListByPreference(
+                        routes, routes, routeListingPreference);
+
+        assertThat(routeOrder.get(0).getId()).isEqualTo(TEST_ID_3);
+        assertThat(routeOrder.get(1).getId()).isEqualTo(TEST_ID_4);
+        assertThat(routeOrder.get(2).getId()).isEqualTo(TEST_ID_2);
+        assertThat(routeOrder.get(3).getId()).isEqualTo(TEST_ID_1);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void arrangeRouteListByPreference_useSystemOrderingIsTrue() {
+        RouteListingPreference routeListingPreference =
+                setUpPreferenceList(TEST_PACKAGE_NAME, true);
+        List<MediaRoute2Info> routes = setAvailableRoutesList(TEST_PACKAGE_NAME);
+        when(mRouterManager.getSelectedRoutes(any())).thenReturn(routes);
+
+        List<MediaRoute2Info> routeOrder =
+                Api34Impl.arrangeRouteListByPreference(
+                        routes, routes, routeListingPreference);
+
+        assertThat(routeOrder.get(0).getId()).isEqualTo(TEST_ID_2);
+        assertThat(routeOrder.get(1).getId()).isEqualTo(TEST_ID_3);
+        assertThat(routeOrder.get(2).getId()).isEqualTo(TEST_ID_4);
+        assertThat(routeOrder.get(3).getId()).isEqualTo(TEST_ID_1);
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 14b2dfe..fc402d4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -89,6 +89,7 @@
 import java.time.DateTimeException;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Objects;
@@ -97,7 +98,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
-import java.util.HashMap;
 import java.util.zip.CRC32;
 
 /**
@@ -1753,8 +1753,8 @@
 
         if (previousDensity == null || previousDensity != newDensity) {
             // From nothing to something is a change.
-            DisplayDensityConfiguration.setForcedDisplayDensity(
-                    Display.DEFAULT_DISPLAY, newDensity);
+            DisplayDensityConfiguration.setForcedDisplayDensity(getBaseContext(),
+                    info -> info.type == Display.TYPE_INTERNAL, newDensity);
         }
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index c47bf62..7c588b3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4072,7 +4072,7 @@
 
         @VisibleForTesting
         final class UpgradeController {
-            private static final int SETTINGS_VERSION = 226;
+            private static final int SETTINGS_VERSION = 227;
 
             private final int mUserId;
 
@@ -6266,6 +6266,51 @@
                     currentVersion = 226;
                 }
 
+                // Version 226: Introduces dreaming while postured setting and migrates user from
+                // docked dream trigger to postured dream trigger.
+                if (currentVersion == 226) {
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting dreamOnDock = secureSettings.getSettingLocked(
+                            Secure.SCREENSAVER_ACTIVATE_ON_DOCK);
+                    final Setting dreamsEnabled = secureSettings.getSettingLocked(
+                            Secure.SCREENSAVER_ENABLED);
+                    final boolean dreamOnPosturedDefault = getContext().getResources().getBoolean(
+                            com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault);
+                    final boolean dreamsEnabledByDefault = getContext().getResources().getBoolean(
+                            com.android.internal.R.bool.config_dreamsEnabledByDefault);
+
+                    if (dreamOnPosturedDefault && !dreamOnDock.isNull()
+                            && dreamOnDock.getValue().equals("1")) {
+                        // Disable dock activation and enable postured.
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
+                                Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+                                "0",
+                                null,
+                                true,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
+                                Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+                                "1",
+                                null,
+                                true,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+
+                        // Disable dreams overall, so user doesn't start to unexpectedly see dreams
+                        // enabled when postured.
+                        if (!dreamsEnabledByDefault && !dreamsEnabled.isNull()
+                                && dreamsEnabled.getValue().equals("1")) {
+                            secureSettings.insertSettingOverrideableByRestoreLocked(
+                                    Secure.SCREENSAVER_ENABLED,
+                                    "0",
+                                    null,
+                                    true,
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+
+                    currentVersion = 227;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
index 5ce97eb..6d30f49 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
@@ -34,6 +34,8 @@
     public static final Set<String> ALLOWLIST =
             new ArraySet<String>(Arrays.asList(
                     "adservices",
+                    "autofill",
+                    "app_compat_overrides",
                     "captive_portal_login",
                     "connectivity",
                     "exo",
@@ -41,6 +43,7 @@
                     "netd_native",
                     "network_security",
                     "on_device_personalization",
+                    "testing",
                     "tethering",
                     "tethering_u_or_later_native",
                     "thread_network"
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index 5fdf045..c0c1912 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -29,6 +29,7 @@
         "device_policy_aconfig_flags_lib",
     ],
     flags_packages: [
+        "android.companion.virtualdevice.flags-aconfig",
         "android.security.flags-aconfig",
         "android.permission.flags-aconfig",
         "wear_aconfig_declarations",
@@ -55,6 +56,7 @@
     platform_apis: true,
     manifest: "AndroidManifest.xml",
     flags_packages: [
+        "android.companion.virtualdevice.flags-aconfig",
         "android.security.flags-aconfig",
         "android.permission.flags-aconfig",
     ],
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a044738..9ab8aa8 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -952,7 +952,6 @@
     <uses-permission android:name="android.permission.SETUP_FSVERITY" />
 
     <!-- Permissions required for CTS test - AppFunctionManagerTest -->
-    <uses-permission android:name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" />
     <uses-permission android:name="android.permission.EXECUTE_APP_FUNCTIONS" />
 
     <!-- Permission required for CTS test - CtsNfcTestCases -->
@@ -969,6 +968,13 @@
     <uses-permission android:name="android.permission.MANAGE_INTRUSION_DETECTION_STATE"
         android:featureFlag="android.security.afl_api"/>
 
+    <!-- Permissions required for CTS test - CtsVirtualDevicesTestCases -->
+    <uses-permission android:name="android.permission.ASSOCIATE_COMPANION_DEVICES" />
+    <uses-permission android:name="android.permission.CREATE_VIRTUAL_DEVICE" />
+    <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" />
+    <uses-permission android:name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" />
+    <uses-permission android:name="android.permission.ADD_MIRROR_DISPLAY"
+        android:featureFlag="android.companion.virtualdevice.flags.enable_limited_vdm_role"/>
 
     <!-- Permission required for CTS test - CtsAppTestCases -->
     <uses-permission android:name="android.permission.KILL_UID" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e5e8418..72ae76a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -551,10 +551,13 @@
                 android:exported="true" />
 
         <service android:name=".wallpapers.GradientColorWallpaper"
-            android:featureFlag="android.app.enable_connected_displays_wallpaper"
             android:singleUser="true"
             android:permission="android.permission.BIND_WALLPAPER"
-            android:exported="true" />
+            android:exported="true">
+            <meta-data android:name="android.service.wallpaper"
+                android:resource="@xml/gradient_color_wallpaper">
+            </meta-data>
+        </service>
 
         <activity android:name=".tuner.TunerActivity"
                   android:enabled="false"
@@ -1168,5 +1171,16 @@
             android:exported="false"
             />
 
+	<service
+            android:name="com.google.android.systemui.lowlightclock.LowLightClockDreamService"
+            android:enabled="false"
+            android:exported="false"
+            android:directBootAware="true"
+            android:permission="android.permission.BIND_DREAM_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.dreams.DreamService" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </service>
     </application>
 </manifest>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-nl/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-nl/strings.xml
index 1e28bff..817ef0c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-nl/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-nl/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="accessibility_menu_service_name" msgid="730136711554740131">"toegankelijkheids­menu"</string>
+    <string name="accessibility_menu_service_name" msgid="730136711554740131">"Toegankelijkheids­menu"</string>
     <string name="accessibility_menu_intro" msgid="3164193281544042394">"Het toegankelijkheidsmenu is een groot menu op het scherm waarmee je je apparaat kunt bedienen. Je kunt onder meer je apparaat vergrendelen, het volume en de helderheid aanpassen en screenshots maken."</string>
     <string name="assistant_label" msgid="6796392082252272356">"Assistent"</string>
     <string name="assistant_utterance" msgid="65509599221141377">"Assistent"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml
index f12278a..2afa2aa 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml
@@ -21,7 +21,7 @@
     <string name="previous_button_content_description" msgid="840869171117765966">"Ir para tela anterior"</string>
     <string name="next_button_content_description" msgid="6810058269847364406">"Ir para a próxima tela"</string>
     <string name="accessibility_menu_description" msgid="4458354794093858297">"\"Acessibilidade\" é um grande menu mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string>
-    <string name="accessibility_menu_summary" msgid="340071398148208130">"Controlar o dispositivo com o menu grande"</string>
+    <string name="accessibility_menu_summary" msgid="340071398148208130">"Controle o dispositivo com o menu grande"</string>
     <string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Config. do menu de acessibilidade"</string>
     <string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Botões grandes"</string>
     <string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Aumentar o tamanho dos botões do menu de acessibilidade"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml
index f12278a..2afa2aa 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml
@@ -21,7 +21,7 @@
     <string name="previous_button_content_description" msgid="840869171117765966">"Ir para tela anterior"</string>
     <string name="next_button_content_description" msgid="6810058269847364406">"Ir para a próxima tela"</string>
     <string name="accessibility_menu_description" msgid="4458354794093858297">"\"Acessibilidade\" é um grande menu mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string>
-    <string name="accessibility_menu_summary" msgid="340071398148208130">"Controlar o dispositivo com o menu grande"</string>
+    <string name="accessibility_menu_summary" msgid="340071398148208130">"Controle o dispositivo com o menu grande"</string>
     <string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Config. do menu de acessibilidade"</string>
     <string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Botões grandes"</string>
     <string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Aumentar o tamanho dos botões do menu de acessibilidade"</string>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 01ee2cd5d..bb0d5d7 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -196,6 +196,18 @@
 }
 
 flag {
+    name: "notification_undo_guts_on_config_changed"
+    namespace: "systemui"
+    description: "Fixes a bug where a theme or font change while notification guts were open"
+        " (e.g. the snooze options or notification info) would show an empty notification by"
+        " closing the guts and undoing changes."
+    bug: "379267630"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
    name: "pss_app_selector_recents_split_screen"
    namespace: "systemui"
    description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
@@ -237,6 +249,13 @@
 }
 
 flag {
+  name: "notification_bundle_ui"
+  namespace: "systemui"
+  description: "Three-level group UI for notification bundles"
+  bug: "367996732"
+}
+
+flag {
     name: "notification_background_tint_optimization"
     namespace: "systemui"
     description: "Re-enable the codepath that removed tinting of notifications when the"
@@ -534,6 +553,16 @@
 }
 
 flag {
+   name: "face_scanning_animation_npe_fix"
+   namespace: "systemui"
+   description: "Fix for the face scanning animation NPE"
+   bug: "392032258"
+   metadata {
+       purpose: PURPOSE_BUGFIX
+   }
+}
+
+flag {
     name: "indication_text_a11y_fix"
     namespace: "systemui"
     description: "add double shadow to the indication text at the bottom of the lock screen"
@@ -558,20 +587,6 @@
 }
 
 flag {
-   name: "clock_reactive_variants"
-   namespace: "systemui"
-   description: "Add reactive variant fonts to some clocks"
-   bug: "343495953"
-}
-
-flag {
-   name: "lockscreen_custom_clocks"
-   namespace: "systemui"
-   description: "Enable lockscreen custom clocks"
-   bug: "378486437"
-}
-
-flag {
    name: "faster_unlock_transition"
    namespace: "systemui"
    description: "Faster wallpaper unlock transition"
@@ -616,13 +631,6 @@
 }
 
 flag {
-   name: "quick_settings_visual_haptics_longpress"
-   namespace: "systemui"
-   description: "Enable special visual and haptic effects for quick settings tiles with long-press actions"
-   bug: "229856884"
-}
-
-flag {
    name: "switch_user_on_bg"
    namespace: "systemui"
    description: "Does user switching on a background thread"
@@ -1197,6 +1205,13 @@
 }
 
 flag {
+  name: "glanceable_hub_blurred_background"
+  namespace: "systemui"
+  description: "Allow blurred background on glanceable hub"
+  bug: "389788272"
+}
+
+flag {
   name: "communal_widget_resizing"
   namespace: "systemui"
   description: "Allow resizing of widgets on glanceable hub"
@@ -1318,10 +1333,13 @@
 }
 
 flag {
-  name: "media_controls_drawables_reuse"
+  name: "media_controls_drawables_reuse_bugfix"
   namespace: "systemui"
   description: "Re-use created media drawables for media controls"
   bug: "358402034"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
 
 flag {
@@ -1808,6 +1826,13 @@
 }
 
 flag {
+    name: "notification_row_transparency"
+    namespace: "systemui"
+    description: "Enables transparency on the Notification Shade."
+    bug: "392187268"
+}
+
+flag {
   name: "shade_expands_on_status_bar_long_press"
   namespace: "systemui"
   description: "Expands the shade on long press of any status bar"
@@ -1852,6 +1877,13 @@
 }
 
 flag {
+    name: "double_tap_to_sleep"
+    namespace: "systemui"
+    description: "Enable Double Tap to Sleep"
+    bug: "385194612"
+}
+
+flag{
     name: "gsf_bouncer"
     namespace: "systemui"
     description: "Applies GSF font styles to Bouncer surfaces."
@@ -1935,23 +1967,10 @@
 }
 
 flag {
-    name: "stabilize_heads_up_group"
-    namespace: "systemui"
-    description: "Stabilize heads up groups in VisualStabilityCoordinator"
-    bug: "381864715"
-    metadata {
-      purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
-   name: "magnetic_notification_horizontal_swipe"
+   name: "magnetic_notification_swipes"
    namespace: "systemui"
    description: "Add support for magnetic behavior on horizontal notification swipes."
    bug: "390179908"
-   metadata {
-        purpose: PURPOSE_BUGFIX
-   }
 }
 
 flag {
@@ -1962,6 +1981,13 @@
 }
 
 flag {
+  name: "permission_helper_ui_rich_ongoing"
+  namespace: "systemui"
+  description: "[RONs] Guards inline permission helper for demoting RONs"
+  bug: "379186372"
+}
+
+flag {
     name: "aod_ui_rich_ongoing"
     namespace: "systemui"
     description: "Show a rich ongoing notification on the always-on display (depends on ui_rich_ongoing)"
@@ -1976,4 +2002,21 @@
     metadata {
       purpose: PURPOSE_BUGFIX
     }
-}
\ No newline at end of file
+}
+
+flag {
+   name: "shade_launch_accessibility"
+   namespace: "systemui"
+   description: "Intercept accessibility focus events for the Shade during launch animations to avoid stray TalkBack events."
+   bug: "379222226"
+   metadata {
+        purpose: PURPOSE_BUGFIX
+   }
+}
+
+flag {
+    name: "show_locked_by_your_watch_keyguard_indicator"
+    namespace: "systemui"
+    description: "Show a Locked by your watch indicator on the keyguard when the device is locked by the watch."
+    bug: "387322459"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
index 78ae4af..9545bda 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
@@ -1,9 +1,12 @@
 package com.android.systemui.animation
 
-private const val TAG_WGHT = "wght"
-private const val TAG_WDTH = "wdth"
-private const val TAG_OPSZ = "opsz"
-private const val TAG_ROND = "ROND"
+object GSFAxes {
+    const val WEIGHT = "wght"
+    const val WIDTH = "wdth"
+    const val SLANT = "slnt"
+    const val ROUND = "ROND"
+    const val OPTICAL_SIZE = "opsz"
+}
 
 class FontVariationUtils {
     private var mWeight = -1
@@ -21,7 +24,7 @@
         weight: Int = -1,
         width: Int = -1,
         opticalSize: Int = -1,
-        roundness: Int = -1
+        roundness: Int = -1,
     ): String {
         isUpdated = false
         if (weight >= 0 && mWeight != weight) {
@@ -43,16 +46,20 @@
         }
         var resultString = ""
         if (mWeight >= 0) {
-            resultString += "'$TAG_WGHT' $mWeight"
+            resultString += "'${GSFAxes.WEIGHT}' $mWeight"
         }
         if (mWidth >= 0) {
-            resultString += (if (resultString.isBlank()) "" else ", ") + "'$TAG_WDTH' $mWidth"
+            resultString +=
+                (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.WIDTH}' $mWidth"
         }
         if (mOpticalSize >= 0) {
-            resultString += (if (resultString.isBlank()) "" else ", ") + "'$TAG_OPSZ' $mOpticalSize"
+            resultString +=
+                (if (resultString.isBlank()) "" else ", ") +
+                    "'${GSFAxes.OPTICAL_SIZE}' $mOpticalSize"
         }
         if (mRoundness >= 0) {
-            resultString += (if (resultString.isBlank()) "" else ", ") + "'$TAG_ROND' $mRoundness"
+            resultString +=
+                (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.ROUND}' $mRoundness"
         }
         return if (isUpdated) resultString else ""
     }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index f36f030..694fc8e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -22,7 +22,6 @@
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS;
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
@@ -165,8 +164,7 @@
                         t.show(wallpapers[i].leash);
                         t.setAlpha(wallpapers[i].leash, 1.f);
                     }
-                    if (ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue()
-                            || ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX.isTrue()) {
+                    if (ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX.isTrue()) {
                         resetLauncherAlphaOnDesktopExit(info, launcherTask, leashMap, t);
                     }
                 } else {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
index e74b185..1bb8ae5 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
@@ -40,7 +40,7 @@
 @Composable
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 fun rememberOffsetOverscrollEffect(
-    animationSpec: AnimationSpec<Float> = MaterialTheme.motionScheme.defaultSpatialSpec()
+    animationSpec: AnimationSpec<Float> = MaterialTheme.motionScheme.slowSpatialSpec()
 ): OffsetOverscrollEffect {
     val animationScope = rememberCoroutineScope()
     return remember(animationScope, animationSpec) {
@@ -51,7 +51,7 @@
 @Composable
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 fun rememberOffsetOverscrollEffectFactory(
-    animationSpec: AnimationSpec<Float> = MaterialTheme.motionScheme.defaultSpatialSpec()
+    animationSpec: AnimationSpec<Float> = MaterialTheme.motionScheme.slowSpatialSpec()
 ): OverscrollFactory {
     val animationScope = rememberCoroutineScope()
     return remember(animationScope, animationSpec) {
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
index 0f6e6a7..f490968b 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
@@ -34,7 +34,6 @@
 import dagger.Provides
 import dagger.multibindings.IntoSet
 import javax.inject.Provider
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 @Module(includes = [LockscreenSceneBlueprintModule::class])
 interface LockscreenSceneModule {
@@ -43,7 +42,6 @@
 
     companion object {
 
-        @OptIn(ExperimentalCoroutinesApi::class)
         @Provides
         @SysUISingleton
         @KeyguardRootView
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 824f5a2..3ffbabb 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
@@ -256,6 +256,7 @@
             CommunalBackgroundType.STATIC_GRADIENT -> StaticLinearGradient()
             CommunalBackgroundType.ANIMATED -> AnimatedLinearGradient()
             CommunalBackgroundType.NONE -> BackgroundTopScrim()
+            CommunalBackgroundType.BLUR -> Background()
         }
 
         with(content) {
@@ -314,6 +315,9 @@
     Box(Modifier.matchParentSize().alpha(0.34f).background(scrimOnTopColor))
 }
 
+/** Transparent (nothing) composable for when the background is blurred. */
+@Composable private fun BoxScope.Background() {}
+
 /** The duration to use for the gradient background animation. */
 private const val ANIMATION_DURATION_MS = 10_000
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 30dfa5b..2c6d09a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -19,13 +19,11 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntRect
@@ -35,6 +33,7 @@
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
 import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
+import com.android.systemui.communal.ui.compose.section.CommunalLockSection
 import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
 import com.android.systemui.communal.ui.compose.section.CommunalToDreamButtonSection
 import com.android.systemui.communal.ui.compose.section.HubOnboardingSection
@@ -43,7 +42,6 @@
 import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
 import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import javax.inject.Inject
 import kotlin.math.min
@@ -58,6 +56,7 @@
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     private val dialogFactory: SystemUIDialogFactory,
     private val lockSection: LockSection,
+    private val communalLockSection: CommunalLockSection,
     private val bottomAreaSection: BottomAreaSection,
     private val ambientStatusBarSection: AmbientStatusBarSection,
     private val communalPopupSection: CommunalPopupSection,
@@ -88,12 +87,9 @@
                         with(hubOnboardingSection) { BottomSheet() }
                     }
                     if (communalSettingsInteractor.isV2FlagEnabled()) {
-                        Icon(
-                            painter = painterResource(id = R.drawable.ic_lock),
-                            contentDescription = null,
-                            tint = MaterialTheme.colorScheme.onPrimaryContainer,
-                            modifier = Modifier.element(Communal.Elements.LockIcon),
-                        )
+                        with(communalLockSection) {
+                            LockIcon(modifier = Modifier.element(Communal.Elements.LockIcon))
+                        }
                     } else {
                         with(lockSection) {
                             LockIcon(
@@ -156,14 +152,8 @@
 
                 val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
 
-                val screensaverButtonSizeInt = screensaverButtonSize.roundToPx()
                 val screensaverButtonPlaceable =
-                    screensaverButtonMeasurable?.measure(
-                        Constraints.fixed(
-                            width = screensaverButtonSizeInt,
-                            height = screensaverButtonSizeInt,
-                        )
-                    )
+                    screensaverButtonMeasurable?.measure(noMinConstraints)
 
                 val communalGridPlaceable =
                     communalGridMeasurable.measure(
@@ -181,12 +171,12 @@
                     screensaverButtonPlaceable?.place(
                         x =
                             constraints.maxWidth -
-                                screensaverButtonSizeInt -
-                                screensaverButtonPaddingInt,
+                                screensaverButtonPaddingInt -
+                                screensaverButtonPlaceable.width,
                         y =
                             constraints.maxHeight -
-                                screensaverButtonSizeInt -
-                                screensaverButtonPaddingInt,
+                                screensaverButtonPaddingInt -
+                                screensaverButtonPlaceable.height,
                     )
                 }
             }
@@ -194,7 +184,6 @@
     }
 
     companion object {
-        private val screensaverButtonSize: Dp = 64.dp
         private val screensaverButtonPadding: Dp = 24.dp
 
         // TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalLockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalLockSection.kt
new file mode 100644
index 0000000..eab2b87
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalLockSection.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2025 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.communal.ui.compose.section
+
+import android.content.Context
+import android.util.DisplayMetrics
+import android.view.WindowManager
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.ElementKey
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.communal.ui.binder.CommunalLockIconViewBinder
+import com.android.systemui.communal.ui.viewmodel.CommunalLockIconViewModel
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LongPressHandlingViewLogger
+import com.android.systemui.log.dagger.LongPressTouchLog
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.VibratorHelper
+import dagger.Lazy
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+class CommunalLockSection
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val windowManager: WindowManager,
+    private val authController: AuthController,
+    private val viewModel: Lazy<CommunalLockIconViewModel>,
+    private val falsingManager: Lazy<FalsingManager>,
+    private val vibratorHelper: Lazy<VibratorHelper>,
+    private val featureFlags: FeatureFlagsClassic,
+    @LongPressTouchLog private val logBuffer: LogBuffer,
+) {
+    @Composable
+    fun ContentScope.LockIcon(modifier: Modifier = Modifier) {
+        val context = LocalContext.current
+
+        AndroidView(
+            factory = { context ->
+                DeviceEntryIconView(
+                        context,
+                        null,
+                        logger = LongPressHandlingViewLogger(logBuffer, tag = TAG),
+                    )
+                    .apply {
+                        id = R.id.device_entry_icon_view
+                        CommunalLockIconViewBinder.bind(
+                            applicationScope,
+                            this,
+                            viewModel.get(),
+                            falsingManager.get(),
+                            vibratorHelper.get(),
+                        )
+                    }
+            },
+            modifier =
+                modifier.element(LockIconElementKey).layout { measurable, _ ->
+                    val lockIconBounds = lockIconBounds(context)
+                    val placeable =
+                        measurable.measure(
+                            Constraints.fixed(
+                                width = lockIconBounds.width,
+                                height = lockIconBounds.height,
+                            )
+                        )
+                    layout(
+                        width = placeable.width,
+                        height = placeable.height,
+                        alignmentLines =
+                            mapOf(
+                                BlueprintAlignmentLines.LockIcon.Left to lockIconBounds.left,
+                                BlueprintAlignmentLines.LockIcon.Top to lockIconBounds.top,
+                                BlueprintAlignmentLines.LockIcon.Right to lockIconBounds.right,
+                                BlueprintAlignmentLines.LockIcon.Bottom to lockIconBounds.bottom,
+                            ),
+                    ) {
+                        placeable.place(0, 0)
+                    }
+                },
+        )
+    }
+
+    /** Returns the bounds of the lock icon, in window view coordinates. */
+    private fun lockIconBounds(context: Context): IntRect {
+        val windowViewBounds = windowManager.currentWindowMetrics.bounds
+        var widthPx = windowViewBounds.right.toFloat()
+        if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
+            val insets = windowManager.currentWindowMetrics.windowInsets
+            // Assumed to be initially neglected as there are no left or right insets in portrait.
+            // However, on landscape, these insets need to included when calculating the midpoint.
+            @Suppress("DEPRECATION")
+            widthPx -= (insets.systemWindowInsetLeft + insets.systemWindowInsetRight).toFloat()
+        }
+        val defaultDensity =
+            DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
+                DisplayMetrics.DENSITY_DEFAULT.toFloat()
+        val lockIconRadiusPx = (defaultDensity * 36).toInt()
+
+        val scaleFactor = authController.scaleFactor
+        val bottomPaddingPx =
+            context.resources.getDimensionPixelSize(
+                com.android.systemui.customization.R.dimen.lock_icon_margin_bottom
+            )
+        val heightPx = windowViewBounds.bottom.toFloat()
+        val (center, radius) =
+            Pair(
+                IntOffset(
+                    x = (widthPx / 2).toInt(),
+                    y = (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt(),
+                ),
+                (lockIconRadiusPx * scaleFactor).toInt(),
+            )
+
+        return IntRect(center, radius)
+    }
+
+    companion object {
+        private const val TAG = "CommunalLockSection"
+    }
+}
+
+private val LockIconElementKey = ElementKey("CommunalLockIcon")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
index 9421596..13d551a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
@@ -16,14 +16,37 @@
 
 package com.android.systemui.communal.ui.compose.section
 
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.CornerSize
 import androidx.compose.material3.IconButtonDefaults
 import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.RoundRect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.toRect
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.res.stringResource
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
 import com.android.compose.PlatformIconButton
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.ui.compose.extensions.observeTaps
 import com.android.systemui.communal.ui.viewmodel.CommunalToDreamButtonViewModel
 import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.res.R
@@ -43,23 +66,111 @@
 
         val viewModel =
             rememberViewModel("CommunalToDreamButtonSection") { viewModelFactory.create() }
-        val shouldShowDreamButtonOnHub by
-            viewModel.shouldShowDreamButtonOnHub.collectAsStateWithLifecycle(false)
 
-        if (!shouldShowDreamButtonOnHub) {
+        if (!viewModel.shouldShowDreamButtonOnHub) {
             return
         }
 
-        PlatformIconButton(
-            onClick = { viewModel.onShowDreamButtonTap() },
-            iconResource = R.drawable.ic_screensaver_auto,
-            contentDescription =
-                stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
-            colors =
-                IconButtonDefaults.filledIconButtonColors(
-                    contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
-                    containerColor = MaterialTheme.colorScheme.primaryContainer,
-                ),
+        if (viewModel.shouldShowTooltip) {
+            Column(
+                modifier =
+                    Modifier.widthIn(max = tooltipMaxWidth).pointerInput(Unit) {
+                        observeTaps { viewModel.setDreamButtonTooltipDismissed() }
+                    }
+            ) {
+                Tooltip(
+                    pointerOffsetDp = buttonSize.div(2),
+                    text = stringResource(R.string.glanceable_hub_to_dream_button_tooltip),
+                )
+                GoToDreamButton(
+                    modifier = Modifier.width(buttonSize).height(buttonSize).align(Alignment.End)
+                ) {
+                    viewModel.onShowDreamButtonTap()
+                }
+            }
+        } else {
+            GoToDreamButton(modifier = Modifier.width(buttonSize).height(buttonSize)) {
+                viewModel.onShowDreamButtonTap()
+            }
+        }
+    }
+
+    companion object {
+        private val buttonSize = 64.dp
+        private val tooltipMaxWidth = 350.dp
+    }
+}
+
+@Composable
+private fun GoToDreamButton(modifier: Modifier, onClick: () -> Unit) {
+    PlatformIconButton(
+        modifier = modifier,
+        onClick = onClick,
+        iconResource = R.drawable.ic_screensaver_auto,
+        contentDescription = stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
+        colors =
+            IconButtonDefaults.filledIconButtonColors(
+                contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+                containerColor = MaterialTheme.colorScheme.primaryContainer,
+            ),
+    )
+}
+
+@Composable
+private fun Tooltip(pointerOffsetDp: Dp, text: String) {
+    Surface(
+        color = MaterialTheme.colorScheme.surface,
+        shape = TooltipShape(pointerSizeDp = 12.dp, pointerOffsetDp = pointerOffsetDp),
+    ) {
+        Text(
+            modifier = Modifier.padding(start = 32.dp, top = 16.dp, end = 32.dp, bottom = 32.dp),
+            color = MaterialTheme.colorScheme.onSurface,
+            text = text,
         )
     }
+
+    Spacer(modifier = Modifier.height(4.dp))
+}
+
+private class TooltipShape(private val pointerSizeDp: Dp, private val pointerOffsetDp: Dp) : Shape {
+
+    override fun createOutline(
+        size: Size,
+        layoutDirection: LayoutDirection,
+        density: Density,
+    ): Outline {
+
+        val pointerSizePx = with(density) { pointerSizeDp.toPx() }
+        val pointerOffsetPx = with(density) { pointerOffsetDp.toPx() }
+        val cornerRadius = CornerRadius(CornerSize(16.dp).toPx(size, density))
+        val bubbleSize = size.copy(height = size.height - pointerSizePx)
+
+        val path =
+            Path().apply {
+                addRoundRect(
+                    RoundRect(
+                        rect = bubbleSize.toRect(),
+                        topLeft = cornerRadius,
+                        topRight = cornerRadius,
+                        bottomRight = cornerRadius,
+                        bottomLeft = cornerRadius,
+                    )
+                )
+                addPath(
+                    Path().apply {
+                        moveTo(0f, 0f)
+                        lineTo(pointerSizePx / 2f, pointerSizePx)
+                        lineTo(pointerSizePx, 0f)
+                        close()
+                    },
+                    offset =
+                        Offset(
+                            x = bubbleSize.width - pointerOffsetPx - pointerSizePx / 2f,
+                            y = bubbleSize.height,
+                        ),
+                )
+            }
+
+        return Outline.Generic(path)
+    }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
index d022150..500527f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
@@ -57,9 +57,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
 import com.android.systemui.log.LongPressHandlingViewLogger
 import com.android.systemui.res.R
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-@ExperimentalCoroutinesApi
 @Composable
 fun AlternateBouncer(
     alternateBouncerDependencies: AlternateBouncerDependencies,
@@ -127,7 +125,6 @@
     }
 }
 
-@ExperimentalCoroutinesApi
 @Composable
 private fun StatusMessage(
     viewModel: AlternateBouncerMessageAreaViewModel,
@@ -156,7 +153,6 @@
     }
 }
 
-@ExperimentalCoroutinesApi
 @Composable
 private fun DeviceEntryIcon(
     viewModel: AlternateBouncerUdfpsIconViewModel,
@@ -179,7 +175,6 @@
 }
 
 /** TODO (b/353955910): Validate accessibility CUJs */
-@ExperimentalCoroutinesApi
 @Composable
 private fun UdfpsA11yOverlay(
     viewModel: AlternateBouncerUdfpsAccessibilityOverlayViewModel,
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 478970f..d341702 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
@@ -45,7 +45,6 @@
 import com.android.systemui.keyguard.ui.composable.section.TopAreaSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiAod
 import java.util.Optional
 import javax.inject.Inject
 import kotlin.math.roundToInt
@@ -130,9 +129,7 @@
                             if (!isShadeLayoutWide && !isBypassEnabled) {
                                 Box(modifier = Modifier.weight(weight = 1f)) {
                                     Column(Modifier.align(alignment = Alignment.TopStart)) {
-                                        if (PromotedNotificationUiAod.isEnabled) {
-                                            AodPromotedNotification()
-                                        }
+                                        AodPromotedNotificationArea()
                                         AodNotificationIcons(
                                             modifier = Modifier.padding(start = aodIconPadding)
                                         )
@@ -145,9 +142,7 @@
                                 }
                             } else {
                                 Column {
-                                    if (PromotedNotificationUiAod.isEnabled) {
-                                        AodPromotedNotification()
-                                    }
+                                    AodPromotedNotificationArea()
                                     AodNotificationIcons(
                                         modifier = Modifier.padding(start = aodIconPadding)
                                     )
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 0ff567b..d8b3f74 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
@@ -19,12 +19,11 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.dimensionResource
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ContentScope
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardMediaViewModel
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
 import com.android.systemui.media.controls.ui.view.MediaHost
@@ -38,7 +37,7 @@
 constructor(
     private val mediaCarouselController: MediaCarouselController,
     @param:Named(MediaModule.KEYGUARD) private val mediaHost: MediaHost,
-    private val keyguardMediaViewModel: KeyguardMediaViewModel,
+    private val keyguardMediaViewModelFactory: KeyguardMediaViewModel.Factory,
 ) {
 
     @Composable
@@ -46,7 +45,10 @@
         isShadeLayoutWide: Boolean,
         modifier: Modifier = Modifier,
     ) {
-        val isMediaVisible by keyguardMediaViewModel.isMediaVisible.collectAsStateWithLifecycle()
+        val viewModel =
+            rememberViewModel(traceName = "KeyguardMediaCarousel") {
+                keyguardMediaViewModelFactory.create()
+            }
         val horizontalPadding =
             if (isShadeLayoutWide) {
                 dimensionResource(id = R.dimen.notification_side_paddings)
@@ -55,7 +57,7 @@
                     dimensionResource(id = R.dimen.notification_panel_margin_horizontal)
             }
         MediaCarousel(
-            isVisible = isMediaVisible,
+            isVisible = viewModel.isMediaVisible,
             mediaHost = mediaHost,
             modifier = modifier.fillMaxWidth().padding(horizontal = horizontalPadding),
             carouselController = mediaCarouselController,
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 b66690c..abf7fdc 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
@@ -54,6 +54,7 @@
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
 import com.android.systemui.statusbar.notification.promoted.AODPromotedNotification
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiAod
 import com.android.systemui.statusbar.notification.promoted.ui.viewmodel.AODPromotedNotificationViewModel
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
@@ -110,8 +111,23 @@
     }
 
     @Composable
-    fun AodPromotedNotification() {
-        AODPromotedNotification(aodPromotedNotificationViewModelFactory)
+    fun AodPromotedNotificationArea(modifier: Modifier = Modifier) {
+        if (!PromotedNotificationUiAod.isEnabled) {
+            return
+        }
+
+        val isVisible by
+            keyguardRootViewModel.isAodPromotedNotifVisible.collectAsStateWithLifecycle()
+        val burnIn = rememberBurnIn(clockInteractor)
+
+        AnimatedVisibility(
+            visible = isVisible,
+            enter = fadeIn(),
+            exit = fadeOut(),
+            modifier = modifier.burnInAware(aodBurnInViewModel, burnIn.parameters),
+        ) {
+            AODPromotedNotification(aodPromotedNotificationViewModelFactory)
+        }
     }
 
     @Composable
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 25b673b..f0d3f3e 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
@@ -142,6 +142,7 @@
         mutableStateOf<FooterActionsForegroundServicesButtonViewModel?>(null)
     }
     var userSwitcher by remember { mutableStateOf<FooterActionsButtonViewModel?>(null) }
+    var power by remember { mutableStateOf<FooterActionsButtonViewModel?>(null) }
 
     LaunchedEffect(
         context,
@@ -161,6 +162,7 @@
             launch { viewModel.security.collect { security = it } }
             launch { viewModel.foregroundServices.collect { foregroundServices = it } }
             launch { viewModel.userSwitcher.collect { userSwitcher = it } }
+            launch { viewModel.power.collect { power = it } }
         }
     }
 
@@ -220,7 +222,7 @@
             foregroundServices?.let { ForegroundServicesButton(it) }
             userSwitcher?.let { IconButton(it, Modifier.sysuiResTag("multi_user_switch")) }
             IconButton(viewModel.settings, Modifier.sysuiResTag("settings_button_container"))
-            viewModel.power?.let { IconButton(it, Modifier.sysuiResTag("pm_lite")) }
+            power?.let { IconButton(it, Modifier.sysuiResTag("pm_lite")) }
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index d7545cb..2255677 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSettingsTransition
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToSplitShadeTransition
-import com.android.systemui.scene.ui.composable.transitions.notificationsShadeToQuickSettingsShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettingsTransition
 import com.android.systemui.scene.ui.composable.transitions.toNotificationsShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.toQuickSettingsShadeTransition
@@ -172,13 +171,6 @@
         toQuickSettingsShadeTransition()
     }
     from(
-        Overlays.NotificationsShade,
-        to = Overlays.QuickSettingsShade,
-        cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
-    ) {
-        notificationsShadeToQuickSettingsShadeTransition()
-    }
-    from(
         Scenes.Gone,
         to = Overlays.NotificationsShade,
         key = SlightlyFasterShadeCollapse,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
index 6738b97..1423d4a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.scene.ui.composable
 
 import androidx.compose.runtime.snapshotFlow
@@ -26,7 +24,6 @@
 import com.android.compose.animation.scene.observableTransitionState
 import com.android.systemui.scene.shared.model.SceneDataSource
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.flatMapLatest
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromNotificationsShadeToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromNotificationsShadeToQuickSettingsShadeTransition.kt
deleted file mode 100644
index 9a7f99f..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromNotificationsShadeToQuickSettingsShadeTransition.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.scene.ui.composable.transitions
-
-import androidx.compose.animation.core.tween
-import com.android.compose.animation.scene.TransitionBuilder
-import kotlin.time.Duration.Companion.milliseconds
-
-fun TransitionBuilder.notificationsShadeToQuickSettingsShadeTransition(
-    durationScale: Double = 1.0
-) {
-    spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
-}
-
-private val DefaultDuration = 300.milliseconds
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index b4c6003..73b0750 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -17,11 +17,8 @@
 package com.android.systemui.scene.ui.composable.transitions
 
 import androidx.compose.animation.core.tween
-import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.TransitionBuilder
-import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
 import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.notifications.ui.composable.NotificationsShade
@@ -31,9 +28,6 @@
 
 fun TransitionBuilder.toNotificationsShadeTransition(durationScale: Double = 1.0) {
     spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
-    distance = UserActionDistance { _, shadeContentKey, _ ->
-        calculateShadePanelTargetPositionY(shadeContentKey)
-    }
 
     // Ensure the clock isn't clipped by the shade outline during the transition from lockscreen.
     sharedElement(
@@ -50,12 +44,4 @@
     fractionRange(start = .5f) { fade(Notifications.Elements.NotificationScrim) }
 }
 
-/** Returns the Y position of the bottom of the shade container panel within [shadeOverlayKey]. */
-fun UserActionDistanceScope.calculateShadePanelTargetPositionY(shadeOverlayKey: ContentKey): Float {
-    val marginTop = OverlayShade.Elements.Panel.targetOffset(shadeOverlayKey)?.y ?: 0f
-    val panelHeight =
-        OverlayShade.Elements.Panel.targetSize(shadeOverlayKey)?.height?.toFloat() ?: 0f
-    return marginTop + panelHeight
-}
-
 private val DefaultDuration = 300.milliseconds
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
index c9fbb4d..43aa358 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
@@ -19,18 +19,13 @@
 import androidx.compose.animation.core.tween
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.TransitionBuilder
-import com.android.compose.animation.scene.UserActionDistance
 import com.android.systemui.shade.ui.composable.OverlayShade
 import kotlin.time.Duration.Companion.milliseconds
 
 fun TransitionBuilder.toQuickSettingsShadeTransition(durationScale: Double = 1.0) {
     spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
-    distance = UserActionDistance { _, shadeContentKey, _ ->
-        calculateShadePanelTargetPositionY(shadeContentKey)
-    }
 
     translate(OverlayShade.Elements.Panel, Edge.Top)
-
     fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index bdd0da9..4e10ff6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -67,11 +67,10 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.compose.modifiers.sysuiResTag
-import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
-import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
 import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
 import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.res.R
+import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider
 import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderState
 import kotlin.math.round
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -103,6 +102,10 @@
     }
 
     val value by valueState(state)
+    val interactionSource = remember { MutableInteractionSource() }
+    val hapticsViewModel: SliderHapticsViewModel? =
+        setUpHapticsViewModel(value, state.valueRange, interactionSource, hapticsViewModelFactory)
+
     Column(modifier = modifier.animateContentSize(), verticalArrangement = Arrangement.Top) {
         Row(
             horizontalArrangement = Arrangement.spacedBy(12.dp),
@@ -127,8 +130,14 @@
         Slider(
             value = value,
             valueRange = state.valueRange,
-            onValueChange = onValueChange,
-            onValueChangeFinished = onValueChangeFinished,
+            onValueChange = { newValue ->
+                hapticsViewModel?.addVelocityDataPoint(newValue)
+                onValueChange(newValue)
+            },
+            onValueChangeFinished = {
+                hapticsViewModel?.onValueChangeEnded()
+                onValueChangeFinished?.invoke()
+            },
             enabled = state.isEnabled,
             modifier =
                 Modifier.height(40.dp)
@@ -210,41 +219,8 @@
 ) {
     val value by valueState(state)
     val interactionSource = remember { MutableInteractionSource() }
-    val sliderStepSize = 1f / (state.valueRange.endInclusive - state.valueRange.start)
     val hapticsViewModel: SliderHapticsViewModel? =
-        hapticsViewModelFactory?.let {
-            rememberViewModel(traceName = "SliderHapticsViewModel") {
-                it.create(
-                    interactionSource,
-                    state.valueRange,
-                    Orientation.Horizontal,
-                    SliderHapticFeedbackConfig(
-                        lowerBookendScale = 0.2f,
-                        progressBasedDragMinScale = 0.2f,
-                        progressBasedDragMaxScale = 0.5f,
-                        deltaProgressForDragThreshold = 0f,
-                        additionalVelocityMaxBump = 0.2f,
-                        maxVelocityToScale = 0.1f, /* slider progress(from 0 to 1) per sec */
-                        sliderStepSize = sliderStepSize,
-                    ),
-                    SeekableSliderTrackerConfig(
-                        lowerBookendThreshold = 0f,
-                        upperBookendThreshold = 1f,
-                    ),
-                )
-            }
-        }
-    var lastDiscreteStep by remember { mutableFloatStateOf(round(value)) }
-    LaunchedEffect(value) {
-        snapshotFlow { value }
-            .map { round(it) }
-            .filter { it != lastDiscreteStep }
-            .distinctUntilChanged()
-            .collect { discreteStep ->
-                lastDiscreteStep = discreteStep
-                hapticsViewModel?.onValueChange(discreteStep)
-            }
-    }
+        setUpHapticsViewModel(value, state.valueRange, interactionSource, hapticsViewModelFactory)
 
     PlatformSlider(
         modifier =
@@ -357,3 +333,36 @@
         content = { Icon(modifier = Modifier.size(24.dp), icon = icon) },
     )
 }
+
+@Composable
+fun setUpHapticsViewModel(
+    value: Float,
+    valueRange: ClosedFloatingPointRange<Float>,
+    interactionSource: MutableInteractionSource,
+    hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
+): SliderHapticsViewModel? {
+    return hapticsViewModelFactory?.let {
+        rememberViewModel(traceName = "SliderHapticsViewModel") {
+                it.create(
+                    interactionSource,
+                    valueRange,
+                    Orientation.Horizontal,
+                    VolumeHapticsConfigsProvider.sliderHapticFeedbackConfig(valueRange),
+                    VolumeHapticsConfigsProvider.seekableSliderTrackerConfig,
+                )
+            }
+            .also { hapticsViewModel ->
+                var lastDiscreteStep by remember { mutableFloatStateOf(round(value)) }
+                LaunchedEffect(value) {
+                    snapshotFlow { value }
+                        .map { round(it) }
+                        .filter { it != lastDiscreteStep }
+                        .distinctUntilChanged()
+                        .collect { discreteStep ->
+                            lastDiscreteStep = discreteStep
+                            hapticsViewModel.onValueChange(discreteStep)
+                        }
+                }
+            }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index ab3f639..70ff47b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -127,7 +127,14 @@
                 directionChangeSlop = layoutImpl.directionChangeSlop,
             )
 
-        return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation, gestureContext)
+        return createSwipeAnimation(
+            layoutImpl,
+            result,
+            isUpOrLeft,
+            orientation,
+            gestureContext,
+            layoutImpl.decayAnimationSpec,
+        )
     }
 
     private fun resolveSwipeSource(startedPosition: Offset): SwipeSource.Resolved? {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
index eee27b7..41279d3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
@@ -70,6 +70,7 @@
                 distance = 1f,
                 gestureContext =
                     ProvidedGestureContext(dragOffset = 0f, direction = InputDirection.Max),
+                decayAnimationSpec = layoutImpl.decayAnimationSpec,
             )
 
         animateProgress(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index f6d40ee..72bb82b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -17,6 +17,7 @@
 package com.android.compose.animation.scene
 
 import androidx.annotation.FloatRange
+import androidx.compose.animation.rememberSplineBasedDecay
 import androidx.compose.foundation.LocalOverscrollFactory
 import androidx.compose.foundation.OverscrollEffect
 import androidx.compose.foundation.OverscrollFactory
@@ -747,6 +748,7 @@
     val layoutDirection = LocalLayoutDirection.current
     val defaultEffectFactory = checkNotNull(LocalOverscrollFactory.current)
     val animationScope = rememberCoroutineScope()
+    val decayAnimationSpec = rememberSplineBasedDecay<Float>()
     val layoutImpl = remember {
         SceneTransitionLayoutImpl(
                 state = state as MutableSceneTransitionLayoutStateImpl,
@@ -762,6 +764,7 @@
                 lookaheadScope = lookaheadScope,
                 directionChangeSlop = directionChangeSlop,
                 defaultEffectFactory = defaultEffectFactory,
+                decayAnimationSpec = decayAnimationSpec,
             )
             .also { onLayoutImpl?.invoke(it) }
     }
@@ -801,6 +804,7 @@
         layoutImpl.swipeSourceDetector = swipeSourceDetector
         layoutImpl.swipeDetector = swipeDetector
         layoutImpl.transitionInterceptionThreshold = transitionInterceptionThreshold
+        layoutImpl.decayAnimationSpec = decayAnimationSpec
     }
 
     layoutImpl.Content(modifier)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 585da06..53996d2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -17,6 +17,7 @@
 package com.android.compose.animation.scene
 
 import androidx.annotation.VisibleForTesting
+import androidx.compose.animation.core.DecayAnimationSpec
 import androidx.compose.foundation.OverscrollFactory
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.gestures.Orientation
@@ -82,6 +83,7 @@
     internal var swipeSourceDetector: SwipeSourceDetector,
     internal var swipeDetector: SwipeDetector,
     internal var transitionInterceptionThreshold: Float,
+    internal var decayAnimationSpec: DecayAnimationSpec<Float>,
     builder: SceneTransitionLayoutScope<InternalContentScope>.() -> Unit,
 
     /**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 3bd37ad..90c45ee 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -21,6 +21,8 @@
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.core.calculateTargetValue
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.runtime.getValue
@@ -42,6 +44,7 @@
     orientation: Orientation,
     distance: Float,
     gestureContext: MutableDragOffsetGestureContext,
+    decayAnimationSpec: DecayAnimationSpec<Float>,
 ): SwipeAnimation<*> {
     return createSwipeAnimation(
         layoutState,
@@ -53,6 +56,7 @@
             error("Computing contentForUserActions requires a SceneTransitionLayoutImpl")
         },
         gestureContext = gestureContext,
+        decayAnimationSpec = decayAnimationSpec,
     )
 }
 
@@ -62,6 +66,7 @@
     isUpOrLeft: Boolean,
     orientation: Orientation,
     gestureContext: MutableDragOffsetGestureContext,
+    decayAnimationSpec: DecayAnimationSpec<Float>,
     distance: Float = DistanceUnspecified,
 ): SwipeAnimation<*> {
     var lastDistance = distance
@@ -106,6 +111,7 @@
         distance = ::distance,
         contentForUserActions = { layoutImpl.contentForUserActions().key },
         gestureContext = gestureContext,
+        decayAnimationSpec = decayAnimationSpec,
     )
 }
 
@@ -117,6 +123,7 @@
     distance: (SwipeAnimation<*>) -> Float,
     contentForUserActions: () -> ContentKey,
     gestureContext: MutableDragOffsetGestureContext,
+    decayAnimationSpec: DecayAnimationSpec<Float>,
 ): SwipeAnimation<*> {
     fun <T : ContentKey> swipeAnimation(fromContent: T, toContent: T): SwipeAnimation<T> {
         return SwipeAnimation(
@@ -128,6 +135,7 @@
             requiresFullDistanceSwipe = result.requiresFullDistanceSwipe,
             distance = distance,
             gestureContext = gestureContext,
+            decayAnimationSpec = decayAnimationSpec,
         )
     }
 
@@ -201,6 +209,7 @@
     private val distance: (SwipeAnimation<T>) -> Float,
     currentContent: T = fromContent,
     private val gestureContext: MutableDragOffsetGestureContext,
+    private val decayAnimationSpec: DecayAnimationSpec<Float>,
 ) : MutableDragOffsetGestureContext by gestureContext {
     /** The [TransitionState.Transition] whose implementation delegates to this [SwipeAnimation]. */
     lateinit var contentTransition: TransitionState.Transition
@@ -367,20 +376,10 @@
 
         check(isAnimatingOffset())
 
-        val motionSpatialSpec = spec ?: layoutState.motionScheme.defaultSpatialSpec()
-
         val velocityConsumed = CompletableDeferred<Float>()
         offsetAnimationRunnable.complete {
-            val result =
-                animatable.animateTo(
-                    targetValue = targetOffset,
-                    animationSpec = motionSpatialSpec,
-                    initialVelocity = initialVelocity,
-                )
-
-            // We are no longer able to consume the velocity, the rest can be consumed by another
-            // component in the hierarchy.
-            velocityConsumed.complete(initialVelocity - result.endState.velocity)
+            val consumed = animateOffset(animatable, targetOffset, initialVelocity, spec)
+            velocityConsumed.complete(consumed)
 
             // Wait for overscroll to finish so that the transition is removed from the STLState
             // only after the overscroll is done, to avoid dropping frame right when the user lifts
@@ -391,6 +390,60 @@
         return velocityConsumed.await()
     }
 
+    private suspend fun animateOffset(
+        animatable: Animatable<Float, AnimationVector1D>,
+        targetOffset: Float,
+        initialVelocity: Float,
+        spec: AnimationSpec<Float>?,
+    ): Float {
+        val initialOffset = animatable.value
+        val decayOffset =
+            decayAnimationSpec.calculateTargetValue(
+                initialVelocity = initialVelocity,
+                initialValue = initialOffset,
+            )
+
+        // The decay animation should only play if decayOffset exceeds targetOffset.
+        val lowerBound = checkNotNull(animatable.lowerBound) { "No lower bound" }
+        val upperBound = checkNotNull(animatable.upperBound) { "No upper bound" }
+        val willDecayReachBounds =
+            when (targetOffset) {
+                lowerBound -> decayOffset <= lowerBound
+                upperBound -> decayOffset >= upperBound
+                else -> error("Target $targetOffset should be $lowerBound or $upperBound")
+            }
+
+        if (willDecayReachBounds) {
+            val result = animatable.animateDecay(initialVelocity, decayAnimationSpec)
+            check(animatable.value == targetOffset) {
+                buildString {
+                    appendLine(
+                        "animatable.value = ${animatable.value} != $targetOffset = targetOffset"
+                    )
+                    appendLine("  initialOffset=$initialOffset")
+                    appendLine("  targetOffset=$targetOffset")
+                    appendLine("  initialVelocity=$initialVelocity")
+                    appendLine("  decayOffset=$decayOffset")
+                    appendLine(
+                        "  animateDecay result: reason=${result.endReason} " +
+                            "value=${result.endState.value} velocity=${result.endState.velocity}"
+                    )
+                }
+            }
+            return initialVelocity - result.endState.velocity
+        }
+
+        val motionSpatialSpec = spec ?: layoutState.motionScheme.defaultSpatialSpec()
+        animatable.animateTo(
+            targetValue = targetOffset,
+            animationSpec = motionSpatialSpec,
+            initialVelocity = initialVelocity,
+        )
+
+        // We consumed the whole velocity.
+        return initialVelocity
+    }
+
     private fun canChangeContent(targetContent: ContentKey): Boolean {
         return when (val transition = contentTransition) {
             is TransitionState.Transition.ChangeScene ->
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 6b43998..06e76d4 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -18,7 +18,9 @@
 
 package com.android.compose.animation.scene
 
+import androidx.compose.animation.SplineBasedFloatDecayAnimationSpec
 import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.generateDecayAnimationSpec
 import androidx.compose.animation.core.spring
 import androidx.compose.foundation.overscroll
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
@@ -119,10 +121,11 @@
         val transitionInterceptionThreshold = 0.05f
         val directionChangeSlop = 10f
 
+        private val density = Density(1f)
         private val layoutImpl =
             SceneTransitionLayoutImpl(
                     state = layoutState,
-                    density = Density(1f),
+                    density = density,
                     layoutDirection = LayoutDirection.Ltr,
                     swipeSourceDetector = DefaultEdgeDetector,
                     swipeDetector = DefaultSwipeDetector,
@@ -134,6 +137,8 @@
                     animationScope = testScope,
                     directionChangeSlop = directionChangeSlop,
                     defaultEffectFactory = defaultEffectFactory,
+                    decayAnimationSpec =
+                        SplineBasedFloatDecayAnimationSpec(density).generateDecayAnimationSpec(),
                 )
                 .apply { setContentsAndLayoutTargetSizeForTest(LAYOUT_SIZE) }
 
@@ -768,6 +773,36 @@
     }
 
     @Test
+    fun startSwipeAnimationFromBound() = runGestureTest {
+        // Swipe down to go to SceneC.
+        mutableUserActionsA = mapOf(Swipe.Down to SceneC)
+
+        val dragController =
+            onDragStarted(
+                position = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f),
+                // Swipe up.
+                overSlop = up(0.5f),
+                // Should be ignored.
+                expectedConsumedOverSlop = 0f,
+            )
+
+        val transition = assertThat(transitionState).isSceneTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneC)
+        assertThat(transition).hasProgress(0f)
+
+        // Swipe down, but not enough to go to SceneC.
+        dragController.onDragStoppedAnimateNow(
+            velocity = velocityThreshold - 0.01f,
+            onAnimationStart = {
+                assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0f)
+            },
+        )
+
+        assertIdle(SceneA)
+    }
+
+    @Test
     fun requireFullDistanceSwipe() = runGestureTest {
         mutableUserActionsA +=
             Swipe.Up to UserActionResult(SceneB, requiresFullDistanceSwipe = true)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 51483a8..358d018 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.compose.nestedscroll
 
 import androidx.compose.foundation.gestures.FlingBehavior
@@ -28,7 +26,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.compose.test.runMonotonicClockTest
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.runTest
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt
index 0245cf2..97fba3d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt
@@ -3,7 +3,6 @@
 import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.TestMonotonicFrameClock
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestCoroutineScheduler
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
@@ -18,7 +17,7 @@
  * Note: Please refer to the documentation for [runTest], as this feature utilizes it. This will
  * provide a comprehensive understanding of all its behaviors.
  */
-@OptIn(ExperimentalTestApi::class, ExperimentalCoroutinesApi::class)
+@OptIn(ExperimentalTestApi::class)
 fun runMonotonicClockTest(block: suspend MonotonicClockTestScope.() -> Unit) = runTest {
     val testScope: TestScope = this
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index f6ff3268..6cc281a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -17,11 +17,13 @@
 import android.content.res.Resources
 import android.graphics.Typeface
 import android.view.LayoutInflater
+import com.android.systemui.animation.GSFAxes
 import com.android.systemui.customization.R
 import com.android.systemui.log.core.MessageBuffer
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockFontAxis
 import com.android.systemui.plugins.clocks.ClockFontAxisSetting
+import com.android.systemui.plugins.clocks.ClockLogger
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockMetadata
 import com.android.systemui.plugins.clocks.ClockPickerConfig
@@ -61,7 +63,7 @@
         }
 
         return if (isClockReactiveVariantsEnabled) {
-            val buffers = messageBuffers ?: ClockMessageBuffers(LogUtil.DEFAULT_MESSAGE_BUFFER)
+            val buffers = messageBuffers ?: ClockMessageBuffers(ClockLogger.DEFAULT_MESSAGE_BUFFER)
             val fontAxes = ClockFontAxis.merge(FlexClockController.FONT_AXES, settings.axes)
             val clockSettings = settings.copy(axes = fontAxes.map { it.toSetting() })
             val typefaceCache =
@@ -107,18 +109,18 @@
         // TODO(b/364681643): Variations for retargetted DIGITAL_CLOCK_FLEX
         val LEGACY_FLEX_LS_VARIATION =
             listOf(
-                ClockFontAxisSetting("wght", 600f),
-                ClockFontAxisSetting("wdth", 100f),
-                ClockFontAxisSetting("ROND", 100f),
-                ClockFontAxisSetting("slnt", 0f),
+                ClockFontAxisSetting(GSFAxes.WEIGHT, 600f),
+                ClockFontAxisSetting(GSFAxes.WIDTH, 100f),
+                ClockFontAxisSetting(GSFAxes.ROUND, 100f),
+                ClockFontAxisSetting(GSFAxes.SLANT, 0f),
             )
 
         val LEGACY_FLEX_AOD_VARIATION =
             listOf(
-                ClockFontAxisSetting("wght", 74f),
-                ClockFontAxisSetting("wdth", 43f),
-                ClockFontAxisSetting("ROND", 100f),
-                ClockFontAxisSetting("slnt", 0f),
+                ClockFontAxisSetting(GSFAxes.WEIGHT, 74f),
+                ClockFontAxisSetting(GSFAxes.WIDTH, 43f),
+                ClockFontAxisSetting(GSFAxes.ROUND, 100f),
+                ClockFontAxisSetting(GSFAxes.SLANT, 0f),
             )
 
         val FLEX_TYPEFACE by lazy {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index e69fa99..cc3769e 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.clocks
 
+import com.android.systemui.animation.GSFAxes
 import com.android.systemui.customization.R
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.AxisType
@@ -122,16 +123,16 @@
         val FONT_AXES =
             listOf(
                 ClockFontAxis(
-                    key = "wght",
+                    key = GSFAxes.WEIGHT,
                     type = AxisType.Float,
-                    minValue = 1f,
+                    minValue = 25f,
                     currentValue = 400f,
                     maxValue = 1000f,
                     name = "Weight",
                     description = "Glyph Weight",
                 ),
                 ClockFontAxis(
-                    key = "wdth",
+                    key = GSFAxes.WIDTH,
                     type = AxisType.Float,
                     minValue = 25f,
                     currentValue = 100f,
@@ -140,7 +141,7 @@
                     description = "Glyph Width",
                 ),
                 ClockFontAxis(
-                    key = "ROND",
+                    key = GSFAxes.ROUND,
                     type = AxisType.Boolean,
                     minValue = 0f,
                     currentValue = 0f,
@@ -149,7 +150,7 @@
                     description = "Glyph Roundness",
                 ),
                 ClockFontAxis(
-                    key = "slnt",
+                    key = GSFAxes.SLANT,
                     type = AxisType.Boolean,
                     minValue = 0f,
                     currentValue = 0f,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 827bd68..e2bbe0f 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -21,6 +21,7 @@
 import android.view.View
 import android.view.ViewGroup.LayoutParams.MATCH_PARENT
 import android.widget.FrameLayout
+import com.android.systemui.animation.GSFAxes
 import com.android.systemui.customization.R
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockAnimations
@@ -125,7 +126,19 @@
             layerController.faceEvents.onThemeChanged(theme)
         }
 
-        override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+        override fun onFontAxesChanged(settings: List<ClockFontAxisSetting>) {
+            var axes = settings
+            if (!isLargeClock) {
+                axes =
+                    axes.map { axis ->
+                        if (axis.key == GSFAxes.WIDTH && axis.value > SMALL_CLOCK_MAX_WDTH) {
+                            axis.copy(value = SMALL_CLOCK_MAX_WDTH)
+                        } else {
+                            axis
+                        }
+                    }
+            }
+
             layerController.events.onFontAxesChanged(axes)
         }
 
@@ -236,6 +249,7 @@
         }
 
     companion object {
+        val SMALL_CLOCK_MAX_WDTH = 120f
         val SMALL_LAYER_CONFIG =
             LayerConfig(
                 timespec = DigitalTimespec.TIME_FULL_FORMAT,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/LogUtil.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/LogUtil.kt
deleted file mode 100644
index 34cb4ef..0000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/LogUtil.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.shared.clocks
-
-import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.core.LogcatOnlyMessageBuffer
-import com.android.systemui.log.core.Logger
-
-object LogUtil {
-    // Used when MessageBuffers are not provided by the host application
-    val DEFAULT_MESSAGE_BUFFER = LogcatOnlyMessageBuffer(LogLevel.INFO)
-
-    // Only intended for use during initialization steps where the correct logger doesn't exist yet
-    val FALLBACK_INIT_LOGGER = Logger(LogcatOnlyMessageBuffer(LogLevel.ERROR), "CLOCK_INIT")
-
-    // Debug is primarially used for tests, but can also be used for tracking down hard issues.
-    val DEBUG_MESSAGE_BUFFER = LogcatOnlyMessageBuffer(LogLevel.DEBUG)
-}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index c40bb9a..55750b5 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -27,11 +27,10 @@
 import androidx.annotation.VisibleForTesting
 import com.android.app.animation.Interpolators
 import com.android.systemui.customization.R
-import com.android.systemui.log.core.Logger
 import com.android.systemui.plugins.clocks.ClockFontAxisSetting
+import com.android.systemui.plugins.clocks.ClockLogger
 import com.android.systemui.shared.clocks.ClockContext
 import com.android.systemui.shared.clocks.DigitTranslateAnimator
-import com.android.systemui.shared.clocks.LogUtil
 import java.util.Locale
 import kotlin.math.abs
 import kotlin.math.max
@@ -40,8 +39,8 @@
 fun clamp(value: Float, minVal: Float, maxVal: Float): Float = max(min(value, maxVal), minVal)
 
 class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
-    protected val logger = Logger(clockCtx.messageBuffer, this::class.simpleName!!)
-        get() = field ?: LogUtil.FALLBACK_INIT_LOGGER
+    protected val logger = ClockLogger(this, clockCtx.messageBuffer, this::class.simpleName!!)
+        get() = field ?: ClockLogger.INIT_LOGGER
 
     @VisibleForTesting
     var isAnimationEnabled = true
@@ -121,11 +120,7 @@
 
     override fun addView(child: View?) {
         if (child == null) return
-        logger.d({ "addView($str1 @$int1)" }) {
-            str1 = child::class.simpleName!!
-            int1 = child.id
-        }
-
+        logger.addView(child)
         super.addView(child)
         (child as? SimpleDigitalClockTextView)?.let {
             it.digitTranslateAnimator = DigitTranslateAnimator(::invalidate)
@@ -135,22 +130,32 @@
     }
 
     fun refreshTime() {
-        logger.d("refreshTime()")
+        logger.refreshTime()
         digitalClockTextViewMap.forEach { (_, textView) -> textView.refreshText() }
     }
 
+    override fun setVisibility(visibility: Int) {
+        logger.setVisibility(visibility)
+        super.setVisibility(visibility)
+    }
+
+    override fun setAlpha(alpha: Float) {
+        logger.setAlpha(alpha)
+        super.setAlpha(alpha)
+    }
+
     override fun invalidate() {
-        logger.d("invalidate()")
+        logger.invalidate()
         super.invalidate()
     }
 
     override fun requestLayout() {
-        logger.d("requestLayout()")
+        logger.requestLayout()
         super.requestLayout()
     }
 
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
-        logger.d("onMeasure()")
+        logger.onMeasure()
         calculateSize(widthMeasureSpec, heightMeasureSpec)?.let { size ->
             setMeasuredDimension(size.x, size.y)
         } ?: run { super.onMeasure(widthMeasureSpec, heightMeasureSpec) }
@@ -162,12 +167,12 @@
     }
 
     override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
-        logger.d("onLayout()")
+        logger.onLayout()
         super.onLayout(changed, left, top, right, bottom)
     }
 
     override fun onDraw(canvas: Canvas) {
-        logger.d("onDraw()")
+        logger.onDraw()
         super.onDraw(canvas)
 
         digitalClockTextViewMap.forEach { (id, textView) ->
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 2b0825f..db39162 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -34,15 +34,15 @@
 import android.view.animation.Interpolator
 import android.widget.TextView
 import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.animation.GSFAxes
 import com.android.systemui.animation.TextAnimator
 import com.android.systemui.customization.R
-import com.android.systemui.log.core.Logger
 import com.android.systemui.plugins.clocks.ClockFontAxisSetting
+import com.android.systemui.plugins.clocks.ClockLogger
 import com.android.systemui.shared.clocks.ClockContext
 import com.android.systemui.shared.clocks.DigitTranslateAnimator
 import com.android.systemui.shared.clocks.DimensionParser
 import com.android.systemui.shared.clocks.FontTextStyle
-import com.android.systemui.shared.clocks.LogUtil
 import java.lang.Thread
 import kotlin.math.max
 import kotlin.math.min
@@ -93,8 +93,8 @@
     private val prevTextBounds = Rect()
     // targetTextBounds holds the state we are interpolating to
     private val targetTextBounds = Rect()
-    protected val logger = Logger(clockCtx.messageBuffer, this::class.simpleName!!)
-        get() = field ?: LogUtil.FALLBACK_INIT_LOGGER
+    protected val logger = ClockLogger(this, clockCtx.messageBuffer, this::class.simpleName!!)
+        get() = field ?: ClockLogger.INIT_LOGGER
 
     private var aodDozingInterpolator: Interpolator? = null
 
@@ -145,7 +145,7 @@
     }
 
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
-        logger.d("onMeasure()")
+        logger.onMeasure()
         super.onMeasure(widthMeasureSpec, heightMeasureSpec)
 
         val layout = this.layout
@@ -206,7 +206,8 @@
     }
 
     override fun onDraw(canvas: Canvas) {
-        logger.d({ "onDraw(); ls: $str1" }) { str1 = textAnimator.textInterpolator.shapedText }
+        logger.onDraw(textAnimator.textInterpolator.shapedText)
+
         val translation = getLocalTranslation()
         canvas.translate(translation.x.toFloat(), translation.y.toFloat())
         digitTranslateAnimator?.let {
@@ -221,14 +222,24 @@
         canvas.translate(-translation.x.toFloat(), -translation.y.toFloat())
     }
 
+    override fun setVisibility(visibility: Int) {
+        logger.setVisibility(visibility)
+        super.setVisibility(visibility)
+    }
+
+    override fun setAlpha(alpha: Float) {
+        logger.setAlpha(alpha)
+        super.setAlpha(alpha)
+    }
+
     override fun invalidate() {
-        logger.d("invalidate()")
+        logger.invalidate()
         super.invalidate()
         (parent as? FlexClockView)?.invalidate()
     }
 
     fun refreshTime() {
-        logger.d("refreshTime()")
+        logger.refreshTime()
         refreshText()
     }
 
@@ -433,7 +444,7 @@
         maxSingleDigitWidth = 0
 
         for (i in 0..9) {
-            lockScreenPaint.getTextBounds(i.toString(), 0, 1, rectForCalculate)
+            lockScreenPaint.getTextBounds("$i", 0, 1, rectForCalculate)
             maxSingleDigitHeight = max(maxSingleDigitHeight, rectForCalculate.height())
             maxSingleDigitWidth = max(maxSingleDigitWidth, rectForCalculate.width())
         }
@@ -490,22 +501,22 @@
             Paint().also { it.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT) }
 
         val AOD_COLOR = Color.WHITE
-        val OPTICAL_SIZE_AXIS = ClockFontAxisSetting("opsz", 144f)
+        val OPTICAL_SIZE_AXIS = ClockFontAxisSetting(GSFAxes.OPTICAL_SIZE, 144f)
         val DEFAULT_LS_VARIATION =
             listOf(
                 OPTICAL_SIZE_AXIS,
-                ClockFontAxisSetting("wght", 400f),
-                ClockFontAxisSetting("wdth", 100f),
-                ClockFontAxisSetting("ROND", 0f),
-                ClockFontAxisSetting("slnt", 0f),
+                ClockFontAxisSetting(GSFAxes.WEIGHT, 400f),
+                ClockFontAxisSetting(GSFAxes.WIDTH, 100f),
+                ClockFontAxisSetting(GSFAxes.ROUND, 0f),
+                ClockFontAxisSetting(GSFAxes.SLANT, 0f),
             )
         val DEFAULT_AOD_VARIATION =
             listOf(
                 OPTICAL_SIZE_AXIS,
-                ClockFontAxisSetting("wght", 200f),
-                ClockFontAxisSetting("wdth", 100f),
-                ClockFontAxisSetting("ROND", 0f),
-                ClockFontAxisSetting("slnt", 0f),
+                ClockFontAxisSetting(GSFAxes.WEIGHT, 200f),
+                ClockFontAxisSetting(GSFAxes.WIDTH, 100f),
+                ClockFontAxisSetting(GSFAxes.ROUND, 0f),
+                ClockFontAxisSetting(GSFAxes.SLANT, 0f),
             )
     }
 }
diff --git a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
index 21d8d46..e11a6d0 100644
--- a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
+++ b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
@@ -32,6 +32,11 @@
         return intSetting(name, if (defaultValue) 1 else 0).map { it != 0 }
     }
 
+    fun setBool(name: String, value: Boolean) {
+        settings.value =
+            settings.value.toMutableMap().apply { this[name] = (if (value) 1 else 0).toString() }
+    }
+
     override suspend fun setInt(name: String, value: Int) {
         settings.value = settings.value.toMutableMap().apply { this[name] = value.toString() }
     }
diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py
deleted file mode 100755
index d78ef5a..0000000
--- a/packages/SystemUI/flag_check.py
+++ /dev/null
@@ -1,138 +0,0 @@
-#! /usr/bin/env python3
-
-import sys
-import re
-import argparse
-
-# partially copied from tools/repohooks/rh/hooks.py
-
-TEST_MSG = """Commit message is missing a "Flag:" line.  It must match one of the
-following case-sensitive regex:
-
-    %s
-
-The Flag: stanza is regex matched and should describe whether your change is behind a flag or flags.
-As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the flag.
-For legacy flags use EXEMPT with your flag name.
-
-Some examples below:
-
-Flag: NONE Repohook Update
-Flag: TEST_ONLY
-Flag: EXEMPT resource only update
-Flag: EXEMPT bugfix
-Flag: EXEMPT refactor
-Flag: com.android.launcher3.enable_twoline_allapps
-Flag: com.google.android.apps.nexuslauncher.zero_state_web_data_loader
-
-Check the git history for more examples. It's a regex matched field. See go/android-flag-directive for more details on various formats.
-"""
-
-def main():
-    """Check the commit message for a 'Flag:' line."""
-    parser = argparse.ArgumentParser(
-        description='Check the commit message for a Flag: line.')
-    parser.add_argument('--msg',
-                        metavar='msg',
-                        type=str,
-                        nargs='?',
-                        default='HEAD',
-                        help='commit message to process.')
-    parser.add_argument(
-        '--files',
-        metavar='files',
-        nargs='?',
-        default='',
-        help=
-        'PREUPLOAD_FILES in repo upload to determine whether the check should run for the files.')
-    parser.add_argument(
-        '--project',
-        metavar='project',
-        type=str,
-        nargs='?',
-        default='',
-        help=
-        'REPO_PROJECT in repo upload to determine whether the check should run for this project.')
-
-    # Parse the arguments
-    args = parser.parse_args()
-    desc = args.msg
-    files = args.files
-    project = args.project
-
-    if not should_run_path(project, files):
-        return
-
-    field = 'Flag'
-    none = 'NONE'
-    testOnly = 'TEST_ONLY'
-    docsOnly = 'DOCS_ONLY'
-    exempt = 'EXEMPT'
-    justification = '<justification>'
-
-    # Aconfig Flag name format = <packageName>.<flagName>
-    # package name - Contains only lowercase alphabets + digits + '.' - Ex: com.android.launcher3
-    # For now alphabets, digits, "_", "." characters are allowed in flag name.
-    # Checks if there is "one dot" between packageName and flagName and not adding stricter format check
-    #common_typos_disable
-    flagName = '([a-zA-Z0-9.]+)([.]+)([a-zA-Z0-9_.]+)'
-
-    # None and Exempt needs justification
-    exemptRegex = fr'{exempt}\s*[a-zA-Z]+'
-    noneRegex = fr'{none}\s*[a-zA-Z]+'
-    #common_typos_enable
-
-    readableRegexMsg = '\n\tFlag: '+none+' '+justification+'\n\tFlag: <packageName>.<flagName>\n\tFlag: ' +exempt+' '+justification+'\n\tFlag: '+testOnly+'\n\tFlag: '+docsOnly
-
-    flagRegex = fr'^{field}: .*$'
-    check_flag = re.compile(flagRegex) #Flag:
-
-    # Ignore case for flag name format.
-    flagNameRegex = fr'(?i)^{field}:\s*({noneRegex}|{flagName}|{testOnly}|{docsOnly}|{exemptRegex})\s*'
-    check_flagName = re.compile(flagNameRegex) #Flag: <flag name format>
-
-    flagError = False
-    foundFlag = []
-    # Check for multiple "Flag:" lines and all lines should match this format
-    for line in desc.splitlines():
-        if check_flag.match(line):
-            if not check_flagName.match(line):
-                flagError = True
-                break
-            foundFlag.append(line)
-
-    # Throw error if
-    # 1. No "Flag:" line is found
-    # 2. "Flag:" doesn't follow right format.
-    if (not foundFlag) or (flagError):
-        error = TEST_MSG % (readableRegexMsg)
-        print(error)
-        sys.exit(1)
-
-    sys.exit(0)
-
-
-def should_run_path(project, files):
-    """Returns a boolean if this check should run with these paths.
-    If you want to check for a particular subdirectory under the path,
-    add a check here, call should_run_files and check for a specific sub dir path in should_run_files.
-    """
-    if not project:
-        return False
-    if project == 'platform/frameworks/base':
-        return should_run_files(files)
-    # Default case, run for all other projects which calls this script.
-    return True
-
-
-def should_run_files(files):
-    """Returns a boolean if this check should run with these files."""
-    if not files:
-        return False
-    if 'packages/SystemUI' in files:
-        return True
-    return False
-
-
-if __name__ == '__main__':
-    main()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index b087ecf..6d75c4c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -46,7 +46,7 @@
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.util.settings.SecureSettings;
@@ -86,7 +86,7 @@
     @Mock
     private IRemoteMagnificationAnimationCallback mAnimationCallback;
     @Mock
-    private OverviewProxyService mOverviewProxyService;
+    private LauncherProxyService mLauncherProxyService;
     @Mock
     private SecureSettings mSecureSettings;
     @Mock
@@ -114,7 +114,7 @@
         assertNotNull(mTestableLooper);
         mMagnification = new MagnificationImpl(getContext(),
                 mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
-                mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
+                mModeSwitchesController, mSysUiState, mLauncherProxyService, mSecureSettings,
                 mDisplayTracker, getContext().getSystemService(DisplayManager.class),
                 mA11yLogger, mIWindowManager, mAccessibilityManager,
                 mViewCaptureAwareWindowManager);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
index b0f81c0..f44769d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
@@ -7,11 +7,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-private const val TAG_WGHT = "wght"
-private const val TAG_WDTH = "wdth"
-private const val TAG_OPSZ = "opsz"
-private const val TAG_ROND = "ROND"
-
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class FontVariationUtilsTest : SysuiTestCase() {
@@ -23,19 +18,22 @@
                 weight = 100,
                 width = 100,
                 opticalSize = -1,
-                roundness = 100
+                roundness = 100,
             )
-        Assert.assertEquals("'$TAG_WGHT' 100, '$TAG_WDTH' 100, '$TAG_ROND' 100", initFvar)
+        Assert.assertEquals(
+            "'${GSFAxes.WEIGHT}' 100, '${GSFAxes.WIDTH}' 100, '${GSFAxes.ROUND}' 100",
+            initFvar,
+        )
         val updatedFvar =
             fontVariationUtils.updateFontVariation(
                 weight = 200,
                 width = 100,
                 opticalSize = 0,
-                roundness = 100
+                roundness = 100,
             )
         Assert.assertEquals(
-            "'$TAG_WGHT' 200, '$TAG_WDTH' 100, '$TAG_OPSZ' 0, '$TAG_ROND' 100",
-            updatedFvar
+            "'${GSFAxes.WEIGHT}' 200, '${GSFAxes.WIDTH}' 100, '${GSFAxes.OPTICAL_SIZE}' 0, '${GSFAxes.ROUND}' 100",
+            updatedFvar,
         )
     }
 
@@ -46,14 +44,14 @@
             weight = 100,
             width = 100,
             opticalSize = 0,
-            roundness = 100
+            roundness = 100,
         )
         val updatedFvar1 =
             fontVariationUtils.updateFontVariation(
                 weight = 100,
                 width = 100,
                 opticalSize = 0,
-                roundness = 100
+                roundness = 100,
             )
         Assert.assertEquals("", updatedFvar1)
         val updatedFvar2 = fontVariationUtils.updateFontVariation()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
index 4c329dc..cebd05d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
@@ -18,20 +18,12 @@
 
 import android.bluetooth.BluetoothLeBroadcast
 import android.bluetooth.BluetoothLeBroadcastMetadata
-import android.content.ContentResolver
-import android.content.applicationContext
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.settingslib.bluetooth.BluetoothEventManager
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
-import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
-import com.android.settingslib.bluetooth.VolumeControlProfile
-import com.android.settingslib.volume.shared.AudioSharingLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -46,14 +38,10 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
 import org.mockito.kotlin.any
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.spy
 import org.mockito.kotlin.times
 import org.mockito.kotlin.whenever
 
@@ -78,7 +66,7 @@
     }
 
     @Test
-    fun testIsAudioSharingOn_flagOff_false() =
+    fun isAudioSharingOn_flagOff_false() =
         with(kosmos) {
             testScope.runTest {
                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(false)
@@ -90,7 +78,7 @@
         }
 
     @Test
-    fun testIsAudioSharingOn_flagOn_notInAudioSharing_false() =
+    fun isAudioSharingOn_flagOn_notInAudioSharing_false() =
         with(kosmos) {
             testScope.runTest {
                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
@@ -103,7 +91,7 @@
         }
 
     @Test
-    fun testIsAudioSharingOn_flagOn_inAudioSharing_true() =
+    fun isAudioSharingOn_flagOn_inAudioSharing_true() =
         with(kosmos) {
             testScope.runTest {
                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
@@ -116,7 +104,7 @@
         }
 
     @Test
-    fun testAudioSourceStateUpdate_notInAudioSharing_returnEmpty() =
+    fun audioSourceStateUpdate_notInAudioSharing_returnEmpty() =
         with(kosmos) {
             testScope.runTest {
                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
@@ -129,7 +117,7 @@
         }
 
     @Test
-    fun testAudioSourceStateUpdate_inAudioSharing_returnUnit() =
+    fun audioSourceStateUpdate_inAudioSharing_returnUnit() =
         with(kosmos) {
             testScope.runTest {
                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
@@ -144,7 +132,7 @@
         }
 
     @Test
-    fun testHandleAudioSourceWhenReady_flagOff_sourceNotAdded() =
+    fun handleAudioSourceWhenReady_flagOff_sourceNotAdded() =
         with(kosmos) {
             testScope.runTest {
                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(false)
@@ -157,7 +145,7 @@
         }
 
     @Test
-    fun testHandleAudioSourceWhenReady_noProfile_sourceNotAdded() =
+    fun handleAudioSourceWhenReady_noProfile_sourceNotAdded() =
         with(kosmos) {
             testScope.runTest {
                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
@@ -171,17 +159,41 @@
         }
 
     @Test
-    fun testHandleAudioSourceWhenReady_hasProfileButAudioSharingOff_sourceNotAdded() =
+    fun handleAudioSourceWhenReady_hasProfileButAudioSharingNeverTriggered_sourceNotAdded() =
         with(kosmos) {
             testScope.runTest {
-                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
                 bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
                     localBluetoothLeBroadcast
                 )
                 val job = launch { underTest.handleAudioSourceWhenReady() }
                 runCurrent()
-                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(false)
+
+                // Verify callback registered for onBroadcastStartedOrStopped
+                verify(localBluetoothLeBroadcast).registerServiceCallBack(any(), any())
+                assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
+                job.cancel()
+            }
+        }
+
+    @Test
+    fun handleAudioSourceWhenReady_audioSharingTriggeredButFailed_sourceNotAdded() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
+                    localBluetoothLeBroadcast
+                )
+                val job = launch { underTest.handleAudioSourceWhenReady() }
+                runCurrent()
+                // Verify callback registered for onBroadcastStartedOrStopped
+                verify(localBluetoothLeBroadcast)
+                    .registerServiceCallBack(any(), callbackCaptor.capture())
+                // Audio sharing started failed, trigger onBroadcastStartFailed
+                whenever(localBluetoothLeBroadcast.isEnabled(null)).thenReturn(false)
+                underTest.startAudioSharing()
+                runCurrent()
+                callbackCaptor.value.onBroadcastStartFailed(0)
                 runCurrent()
 
                 assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
@@ -190,40 +202,54 @@
         }
 
     @Test
-    fun testHandleAudioSourceWhenReady_audioSharingOnButNoPlayback_sourceNotAdded() =
+    fun handleAudioSourceWhenReady_audioSharingTriggeredButMetadataNotReady_sourceNotAdded() =
         with(kosmos) {
             testScope.runTest {
-                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(false)
                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
                 bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
                     localBluetoothLeBroadcast
                 )
                 val job = launch { underTest.handleAudioSourceWhenReady() }
                 runCurrent()
-                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
-                runCurrent()
-
-                assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
-                job.cancel()
-            }
-        }
-
-    @Test
-    fun testHandleAudioSourceWhenReady_audioSharingOnAndPlaybackStarts_sourceAdded() =
-        with(kosmos) {
-            testScope.runTest {
-                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(false)
-                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
-                bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
-                    localBluetoothLeBroadcast
-                )
-                val job = launch { underTest.handleAudioSourceWhenReady() }
-                runCurrent()
-                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
-                runCurrent()
+                // Verify callback registered for onBroadcastStartedOrStopped
                 verify(localBluetoothLeBroadcast)
                     .registerServiceCallBack(any(), callbackCaptor.capture())
                 runCurrent()
+                underTest.startAudioSharing()
+                runCurrent()
+                // Verify callback registered for onBroadcastMetadataChanged
+                verify(localBluetoothLeBroadcast, times(2))
+                    .registerServiceCallBack(any(), callbackCaptor.capture())
+
+                assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
+                job.cancel()
+            }
+        }
+
+    @Test
+    fun handleAudioSourceWhenReady_audioSharingTriggeredAndMetadataReady_sourceAdded() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
+                    localBluetoothLeBroadcast
+                )
+                val job = launch { underTest.handleAudioSourceWhenReady() }
+                runCurrent()
+                // Verify callback registered for onBroadcastStartedOrStopped
+                verify(localBluetoothLeBroadcast)
+                    .registerServiceCallBack(any(), callbackCaptor.capture())
+                // Audio sharing started, trigger onBroadcastStarted
+                whenever(localBluetoothLeBroadcast.isEnabled(null)).thenReturn(true)
+                underTest.startAudioSharing()
+                runCurrent()
+                callbackCaptor.value.onBroadcastStarted(0, 0)
+                runCurrent()
+                // Verify callback registered for onBroadcastMetadataChanged
+                verify(localBluetoothLeBroadcast, times(2))
+                    .registerServiceCallBack(any(), callbackCaptor.capture())
+                runCurrent()
+                // Trigger onBroadcastMetadataChanged (ready to add source)
                 callbackCaptor.value.onBroadcastMetadataChanged(0, bluetoothLeBroadcastMetadata)
                 runCurrent()
 
@@ -231,100 +257,4 @@
                 job.cancel()
             }
         }
-
-    @Test
-    fun testHandleAudioSourceWhenReady_skipInitialValue_noAudioSharing_sourceNotAdded() =
-        with(kosmos) {
-            testScope.runTest {
-                val (broadcast, repository) = setupRepositoryImpl()
-                val interactor =
-                    object :
-                        AudioSharingInteractorImpl(
-                            applicationContext,
-                            localBluetoothManager,
-                            repository,
-                            testDispatcher,
-                        ) {
-                        override suspend fun audioSharingAvailable() = true
-                    }
-                val job = launch { interactor.handleAudioSourceWhenReady() }
-                runCurrent()
-                // Verify callback registered for onBroadcastStartedOrStopped
-                verify(broadcast).registerServiceCallBack(any(), callbackCaptor.capture())
-                runCurrent()
-                // Verify source is not added
-                verify(repository, never()).addSource()
-                job.cancel()
-            }
-        }
-
-    @Test
-    fun testHandleAudioSourceWhenReady_skipInitialValue_newAudioSharing_sourceAdded() =
-        with(kosmos) {
-            testScope.runTest {
-                val (broadcast, repository) = setupRepositoryImpl()
-                val interactor =
-                    object :
-                        AudioSharingInteractorImpl(
-                            applicationContext,
-                            localBluetoothManager,
-                            repository,
-                            testDispatcher,
-                        ) {
-                        override suspend fun audioSharingAvailable() = true
-                    }
-                val job = launch { interactor.handleAudioSourceWhenReady() }
-                runCurrent()
-                // Verify callback registered for onBroadcastStartedOrStopped
-                verify(broadcast).registerServiceCallBack(any(), callbackCaptor.capture())
-                // Audio sharing started, trigger onBroadcastStarted
-                whenever(broadcast.isEnabled(null)).thenReturn(true)
-                callbackCaptor.value.onBroadcastStarted(0, 0)
-                runCurrent()
-                // Verify callback registered for onBroadcastMetadataChanged
-                verify(broadcast, times(2)).registerServiceCallBack(any(), callbackCaptor.capture())
-                runCurrent()
-                // Trigger onBroadcastMetadataChanged (ready to add source)
-                callbackCaptor.value.onBroadcastMetadataChanged(0, bluetoothLeBroadcastMetadata)
-                runCurrent()
-                // Verify source added
-                verify(repository).addSource()
-                job.cancel()
-            }
-        }
-
-    private fun setupRepositoryImpl(): Pair<LocalBluetoothLeBroadcast, AudioSharingRepositoryImpl> {
-        with(kosmos) {
-            val broadcast =
-                mock<LocalBluetoothLeBroadcast> {
-                    on { isProfileReady } doReturn true
-                    on { isEnabled(null) } doReturn false
-                }
-            val assistant =
-                mock<LocalBluetoothLeBroadcastAssistant> { on { isProfileReady } doReturn true }
-            val volumeControl = mock<VolumeControlProfile> { on { isProfileReady } doReturn true }
-            val profileManager =
-                mock<LocalBluetoothProfileManager> {
-                    on { leAudioBroadcastProfile } doReturn broadcast
-                    on { leAudioBroadcastAssistantProfile } doReturn assistant
-                    on { volumeControlProfile } doReturn volumeControl
-                }
-            whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
-            whenever(localBluetoothManager.eventManager).thenReturn(mock<BluetoothEventManager> {})
-
-            val repository =
-                AudioSharingRepositoryImpl(
-                    localBluetoothManager,
-                    com.android.settingslib.volume.data.repository.AudioSharingRepositoryImpl(
-                        mock<ContentResolver> {},
-                        localBluetoothManager,
-                        testScope.backgroundScope,
-                        testScope.testScheduler,
-                        mock<AudioSharingLogger> {},
-                    ),
-                    testDispatcher,
-                )
-            return Pair(broadcast, spy(repository))
-        }
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
index 0d410cf..b6359c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
@@ -116,6 +116,34 @@
         }
 
     @Test
+    fun isDreamButtonTooltipDismissedValue_byDefault_isFalse() =
+        testScope.runTest {
+            val isDreamButtonTooltipDismissed by
+                collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))
+            assertThat(isDreamButtonTooltipDismissed).isFalse()
+        }
+
+    @Test
+    fun isDreamButtonTooltipDismissedValue_onSet_isTrue() =
+        testScope.runTest {
+            val isDreamButtonTooltipDismissed by
+                collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))
+
+            underTest.setDreamButtonTooltipDismissed(MAIN_USER)
+            assertThat(isDreamButtonTooltipDismissed).isTrue()
+        }
+
+    @Test
+    fun isDreamButtonTooltipDismissedValue_onSetForDifferentUser_isStillFalse() =
+        testScope.runTest {
+            val isDreamButtonTooltipDismissed by
+                collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))
+
+            underTest.setDreamButtonTooltipDismissed(SECONDARY_USER)
+            assertThat(isDreamButtonTooltipDismissed).isFalse()
+        }
+
+    @Test
     fun getSharedPreferences_whenFileRestored() =
         testScope.runTest {
             val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER))
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 038ea9c..eb1f1d9 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
@@ -30,6 +30,7 @@
 import android.provider.Settings
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND
 import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
@@ -37,19 +38,19 @@
 import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_BACKGROUND_SETTING
 import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.communal.shared.model.WhenToDream
 import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
 import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
-import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,9 +62,11 @@
 @RunWith(ParameterizedAndroidJunit4::class)
 class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiTestCase() {
     private val kosmos =
-        testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
-    private val testScope = kosmos.testScope
-    private lateinit var underTest: CommunalSettingsRepository
+        testKosmos()
+            .apply { mainResources = mContext.orCreateTestableResources.resources }
+            .useUnconfinedTestDispatcher()
+
+    private val Kosmos.underTest by Kosmos.Fixture { communalSettingsRepository }
 
     init {
         mSetFlagsRule.setFlagsParameterization(flags!!)
@@ -75,98 +78,105 @@
         setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
         setKeyguardFeaturesDisabled(SECONDARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
         setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_FEATURES_NONE)
-        underTest = kosmos.communalSettingsRepository
     }
 
     @EnableFlags(FLAG_COMMUNAL_HUB)
     @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
     @Test
-    fun getFlagEnabled_bothEnabled() {
-        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+    fun getFlagEnabled_bothEnabled() =
+        kosmos.runTest {
+            fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
 
-        assertThat(underTest.getFlagEnabled()).isTrue()
-    }
+            assertThat(underTest.getFlagEnabled()).isTrue()
+        }
 
     @DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
     @Test
-    fun getFlagEnabled_bothDisabled() {
-        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+    fun getFlagEnabled_bothDisabled() =
+        kosmos.runTest {
+            fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
 
-        assertThat(underTest.getFlagEnabled()).isFalse()
-    }
+            assertThat(underTest.getFlagEnabled()).isFalse()
+        }
 
     @DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
     @Test
-    fun getFlagEnabled_onlyClassicFlagEnabled() {
-        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+    fun getFlagEnabled_onlyClassicFlagEnabled() =
+        kosmos.runTest {
+            fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
 
-        assertThat(underTest.getFlagEnabled()).isFalse()
-    }
+            assertThat(underTest.getFlagEnabled()).isFalse()
+        }
 
     @EnableFlags(FLAG_COMMUNAL_HUB)
     @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
     @Test
-    fun getFlagEnabled_onlyTrunkFlagEnabled() {
-        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+    fun getFlagEnabled_onlyTrunkFlagEnabled() =
+        kosmos.runTest {
+            fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
 
-        assertThat(underTest.getFlagEnabled()).isFalse()
-    }
+            assertThat(underTest.getFlagEnabled()).isFalse()
+        }
 
     @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
     @DisableFlags(FLAG_COMMUNAL_HUB)
     @Test
-    fun getFlagEnabled_mobileConfigEnabled() {
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.bool.config_glanceableHubEnabled,
-            true,
-        )
+    fun getFlagEnabled_mobileConfigEnabled() =
+        kosmos.runTest {
+            mContext.orCreateTestableResources.addOverride(
+                com.android.internal.R.bool.config_glanceableHubEnabled,
+                true,
+            )
 
-        assertThat(underTest.getFlagEnabled()).isTrue()
-    }
+            assertThat(underTest.getFlagEnabled()).isTrue()
+        }
 
     @DisableFlags(FLAG_GLANCEABLE_HUB_V2, FLAG_COMMUNAL_HUB)
     @Test
-    fun getFlagEnabled_onlyMobileConfigEnabled() {
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.bool.config_glanceableHubEnabled,
-            true,
-        )
+    fun getFlagEnabled_onlyMobileConfigEnabled() =
+        kosmos.runTest {
+            mContext.orCreateTestableResources.addOverride(
+                com.android.internal.R.bool.config_glanceableHubEnabled,
+                true,
+            )
 
-        assertThat(underTest.getFlagEnabled()).isFalse()
-    }
+            assertThat(underTest.getFlagEnabled()).isFalse()
+        }
 
     @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
     @DisableFlags(FLAG_COMMUNAL_HUB)
     @Test
-    fun getFlagEnabled_onlyMobileFlagEnabled() {
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.bool.config_glanceableHubEnabled,
-            false,
-        )
+    fun getFlagEnabled_onlyMobileFlagEnabled() =
+        kosmos.runTest {
+            mContext.orCreateTestableResources.addOverride(
+                com.android.internal.R.bool.config_glanceableHubEnabled,
+                false,
+            )
 
-        assertThat(underTest.getFlagEnabled()).isFalse()
-    }
+            assertThat(underTest.getFlagEnabled()).isFalse()
+        }
 
     @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
     @DisableFlags(FLAG_COMMUNAL_HUB)
     @Test
-    fun getFlagEnabled_oldFlagIgnored() {
-        // New config flag enabled.
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.bool.config_glanceableHubEnabled,
-            true,
-        )
+    fun getFlagEnabled_oldFlagIgnored() =
+        kosmos.runTest {
+            // New config flag enabled.
+            mContext.orCreateTestableResources.addOverride(
+                com.android.internal.R.bool.config_glanceableHubEnabled,
+                true,
+            )
 
-        // Old config flag disabled.
-        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+            // Old config flag disabled.
+            fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
 
-        assertThat(underTest.getFlagEnabled()).isTrue()
-    }
+            assertThat(underTest.getFlagEnabled()).isTrue()
+        }
 
     @EnableFlags(FLAG_COMMUNAL_HUB)
     @Test
     fun secondaryUserIsInvalid() =
-        testScope.runTest {
+        kosmos.runTest {
             val enabledState by collectLastValue(underTest.getEnabledState(SECONDARY_USER))
 
             assertThat(enabledState?.enabled).isFalse()
@@ -186,7 +196,7 @@
     @DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
     @Test
     fun communalHubFlagIsDisabled() =
-        testScope.runTest {
+        kosmos.runTest {
             val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
             assertThat(enabledState?.enabled).isFalse()
             assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
@@ -195,35 +205,23 @@
     @EnableFlags(FLAG_COMMUNAL_HUB)
     @Test
     fun hubIsDisabledByUser() =
-        testScope.runTest {
-            kosmos.fakeSettings.putIntForUser(
-                Settings.Secure.GLANCEABLE_HUB_ENABLED,
-                0,
-                PRIMARY_USER.id,
-            )
+        kosmos.runTest {
+            fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
             val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
             assertThat(enabledState?.enabled).isFalse()
             assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_USER_SETTING)
 
-            kosmos.fakeSettings.putIntForUser(
-                Settings.Secure.GLANCEABLE_HUB_ENABLED,
-                1,
-                SECONDARY_USER.id,
-            )
+            fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, SECONDARY_USER.id)
             assertThat(enabledState?.enabled).isFalse()
 
-            kosmos.fakeSettings.putIntForUser(
-                Settings.Secure.GLANCEABLE_HUB_ENABLED,
-                1,
-                PRIMARY_USER.id,
-            )
+            fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, PRIMARY_USER.id)
             assertThat(enabledState?.enabled).isTrue()
         }
 
     @EnableFlags(FLAG_COMMUNAL_HUB)
     @Test
     fun hubIsDisabledByDevicePolicy() =
-        testScope.runTest {
+        kosmos.runTest {
             val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
             assertThat(enabledState?.enabled).isTrue()
 
@@ -235,7 +233,7 @@
     @EnableFlags(FLAG_COMMUNAL_HUB)
     @Test
     fun widgetsAllowedForWorkProfile_isFalse_whenDisallowedByDevicePolicy() =
-        testScope.runTest {
+        kosmos.runTest {
             val widgetsAllowedForWorkProfile by
                 collectLastValue(underTest.getAllowedByDevicePolicy(WORK_PROFILE))
             assertThat(widgetsAllowedForWorkProfile).isTrue()
@@ -247,7 +245,7 @@
     @EnableFlags(FLAG_COMMUNAL_HUB)
     @Test
     fun hubIsEnabled_whenDisallowedByDevicePolicyForWorkProfile() =
-        testScope.runTest {
+        kosmos.runTest {
             val enabledStateForPrimaryUser by
                 collectLastValue(underTest.getEnabledState(PRIMARY_USER))
             assertThat(enabledStateForPrimaryUser?.enabled).isTrue()
@@ -259,15 +257,11 @@
     @EnableFlags(FLAG_COMMUNAL_HUB)
     @Test
     fun hubIsDisabledByUserAndDevicePolicy() =
-        testScope.runTest {
+        kosmos.runTest {
             val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
             assertThat(enabledState?.enabled).isTrue()
 
-            kosmos.fakeSettings.putIntForUser(
-                Settings.Secure.GLANCEABLE_HUB_ENABLED,
-                0,
-                PRIMARY_USER.id,
-            )
+            fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
             setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
 
             assertThat(enabledState?.enabled).isFalse()
@@ -279,18 +273,19 @@
         }
 
     @Test
+    @DisableFlags(FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
     fun backgroundType_defaultValue() =
-        testScope.runTest {
+        kosmos.runTest {
             val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
             assertThat(backgroundType).isEqualTo(CommunalBackgroundType.ANIMATED)
         }
 
     @Test
     fun backgroundType_verifyAllValues() =
-        testScope.runTest {
+        kosmos.runTest {
             val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
             for (type in CommunalBackgroundType.entries) {
-                kosmos.fakeSettings.putIntForUser(
+                fakeSettings.putIntForUser(
                     GLANCEABLE_HUB_BACKGROUND_SETTING,
                     type.value,
                     PRIMARY_USER.id,
@@ -306,30 +301,71 @@
 
     @Test
     fun screensaverDisabledByUser() =
-        testScope.runTest {
+        kosmos.runTest {
             val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
 
-            kosmos.fakeSettings.putIntForUser(
-                Settings.Secure.SCREENSAVER_ENABLED,
-                0,
-                PRIMARY_USER.id,
-            )
+            fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, 0, PRIMARY_USER.id)
 
             assertThat(enabledState).isFalse()
         }
 
     @Test
     fun screensaverEnabledByUser() =
-        testScope.runTest {
+        kosmos.runTest {
             val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
 
-            kosmos.fakeSettings.putIntForUser(
-                Settings.Secure.SCREENSAVER_ENABLED,
+            fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, 1, PRIMARY_USER.id)
+
+            assertThat(enabledState).isTrue()
+        }
+
+    @Test
+    fun whenToDream_charging() =
+        kosmos.runTest {
+            val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+
+            fakeSettings.putIntForUser(
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
                 1,
                 PRIMARY_USER.id,
             )
 
-            assertThat(enabledState).isTrue()
+            assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_CHARGING)
+        }
+
+    @Test
+    fun whenToDream_docked() =
+        kosmos.runTest {
+            val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+
+            fakeSettings.putIntForUser(
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+                1,
+                PRIMARY_USER.id,
+            )
+
+            assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_DOCKED)
+        }
+
+    @Test
+    fun whenToDream_postured() =
+        kosmos.runTest {
+            val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+
+            fakeSettings.putIntForUser(
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+                1,
+                PRIMARY_USER.id,
+            )
+
+            assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_POSTURED)
+        }
+
+    @Test
+    fun whenToDream_default() =
+        kosmos.runTest {
+            val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+            assertThat(whenToDreamState).isEqualTo(WhenToDream.NEVER)
         }
 
     private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 7ae0577..c9e7a5d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -38,13 +38,9 @@
 import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.common.data.repository.batteryRepository
+import com.android.systemui.common.data.repository.fake
 import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
-import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
-import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
-import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
-import com.android.systemui.communal.data.repository.FakeCommunalSmartspaceRepository
-import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
-import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
 import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
@@ -53,52 +49,49 @@
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
+import com.android.systemui.communal.posturing.data.repository.fake
+import com.android.systemui.communal.posturing.data.repository.posturingRepository
+import com.android.systemui.communal.posturing.shared.model.PosturedState
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.EditModeState
-import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.fakeDockManager
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.plugins.activityStarter
-import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.statusbar.phone.fakeManagedProfileController
 import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.utils.leaks.FakeManagedProfileController
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
 
@@ -107,32 +100,15 @@
  * [CommunalInteractorCommunalDisabledTest].
  */
 @SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(ParameterizedAndroidJunit4::class)
 class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
-    @Mock private lateinit var mainUser: UserInfo
-    @Mock private lateinit var secondaryUser: UserInfo
+    private val mainUser =
+        UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
+    private val secondaryUser = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
 
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
 
-    private lateinit var tutorialRepository: FakeCommunalTutorialRepository
-    private lateinit var communalRepository: FakeCommunalSceneRepository
-    private lateinit var mediaRepository: FakeCommunalMediaRepository
-    private lateinit var widgetRepository: FakeCommunalWidgetRepository
-    private lateinit var smartspaceRepository: FakeCommunalSmartspaceRepository
-    private lateinit var userRepository: FakeUserRepository
-    private lateinit var keyguardRepository: FakeKeyguardRepository
-    private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
-    private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
-    private lateinit var sceneInteractor: SceneInteractor
-    private lateinit var communalSceneInteractor: CommunalSceneInteractor
-    private lateinit var userTracker: FakeUserTracker
-    private lateinit var activityStarter: ActivityStarter
-    private lateinit var userManager: UserManager
-    private lateinit var managedProfileController: FakeManagedProfileController
-
-    private lateinit var underTest: CommunalInteractor
+    private val Kosmos.underTest by Kosmos.Fixture { communalInteractor }
 
     init {
         mSetFlagsRule.setFlagsParameterization(flags)
@@ -140,128 +116,104 @@
 
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        tutorialRepository = kosmos.fakeCommunalTutorialRepository
-        communalRepository = kosmos.fakeCommunalSceneRepository
-        mediaRepository = kosmos.fakeCommunalMediaRepository
-        widgetRepository = kosmos.fakeCommunalWidgetRepository
-        smartspaceRepository = kosmos.fakeCommunalSmartspaceRepository
-        userRepository = kosmos.fakeUserRepository
-        keyguardRepository = kosmos.fakeKeyguardRepository
-        editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter
-        communalPrefsRepository = kosmos.fakeCommunalPrefsRepository
-        sceneInteractor = kosmos.sceneInteractor
-        communalSceneInteractor = kosmos.communalSceneInteractor
-        userTracker = kosmos.fakeUserTracker
-        activityStarter = kosmos.activityStarter
-        userManager = kosmos.userManager
-        managedProfileController = kosmos.fakeManagedProfileController
-
-        whenever(mainUser.isMain).thenReturn(true)
-        whenever(secondaryUser.isMain).thenReturn(false)
-        whenever(userManager.isQuietModeEnabled(any<UserHandle>())).thenReturn(false)
-        whenever(userManager.isManagedProfile(anyInt())).thenReturn(false)
-        userRepository.setUserInfos(listOf(mainUser, secondaryUser))
+        whenever(kosmos.userManager.isQuietModeEnabled(any<UserHandle>())).thenReturn(false)
+        whenever(kosmos.userManager.isManagedProfile(anyInt())).thenReturn(false)
+        kosmos.fakeUserRepository.setUserInfos(listOf(mainUser, secondaryUser))
 
         kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
         mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
-
-        underTest = kosmos.communalInteractor
     }
 
     @Test
     fun communalEnabled_true() =
-        testScope.runTest {
-            userRepository.setSelectedUserInfo(mainUser)
-            runCurrent()
+        kosmos.runTest {
+            fakeUserRepository.setSelectedUserInfo(mainUser)
             assertThat(underTest.isCommunalEnabled.value).isTrue()
         }
 
     @Test
     fun isCommunalAvailable_storageUnlockedAndMainUser_true() =
-        testScope.runTest {
+        kosmos.runTest {
             val isAvailable by collectLastValue(underTest.isCommunalAvailable)
             assertThat(isAvailable).isFalse()
 
-            keyguardRepository.setIsEncryptedOrLockdown(false)
-            userRepository.setSelectedUserInfo(mainUser)
-            keyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+            fakeUserRepository.setSelectedUserInfo(mainUser)
+            fakeKeyguardRepository.setKeyguardShowing(true)
 
             assertThat(isAvailable).isTrue()
         }
 
     @Test
     fun isCommunalAvailable_storageLockedAndMainUser_false() =
-        testScope.runTest {
+        kosmos.runTest {
             val isAvailable by collectLastValue(underTest.isCommunalAvailable)
             assertThat(isAvailable).isFalse()
 
-            keyguardRepository.setIsEncryptedOrLockdown(true)
-            userRepository.setSelectedUserInfo(mainUser)
-            keyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setIsEncryptedOrLockdown(true)
+            fakeUserRepository.setSelectedUserInfo(mainUser)
+            fakeKeyguardRepository.setKeyguardShowing(true)
 
             assertThat(isAvailable).isFalse()
         }
 
     @Test
     fun isCommunalAvailable_storageUnlockedAndSecondaryUser_false() =
-        testScope.runTest {
+        kosmos.runTest {
             val isAvailable by collectLastValue(underTest.isCommunalAvailable)
             assertThat(isAvailable).isFalse()
 
-            keyguardRepository.setIsEncryptedOrLockdown(false)
-            userRepository.setSelectedUserInfo(secondaryUser)
-            keyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+            fakeUserRepository.setSelectedUserInfo(secondaryUser)
+            fakeKeyguardRepository.setKeyguardShowing(true)
 
             assertThat(isAvailable).isFalse()
         }
 
     @Test
     fun isCommunalAvailable_whenKeyguardShowing_true() =
-        testScope.runTest {
+        kosmos.runTest {
             val isAvailable by collectLastValue(underTest.isCommunalAvailable)
             assertThat(isAvailable).isFalse()
 
-            keyguardRepository.setIsEncryptedOrLockdown(false)
-            userRepository.setSelectedUserInfo(mainUser)
-            keyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+            fakeUserRepository.setSelectedUserInfo(mainUser)
+            fakeKeyguardRepository.setKeyguardShowing(true)
 
             assertThat(isAvailable).isTrue()
         }
 
     @Test
     fun isCommunalAvailable_communalDisabled_false() =
-        testScope.runTest {
+        kosmos.runTest {
             mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
 
             val isAvailable by collectLastValue(underTest.isCommunalAvailable)
             assertThat(isAvailable).isFalse()
 
-            keyguardRepository.setIsEncryptedOrLockdown(false)
-            userRepository.setSelectedUserInfo(mainUser)
-            keyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+            fakeUserRepository.setSelectedUserInfo(mainUser)
+            fakeKeyguardRepository.setKeyguardShowing(true)
 
             assertThat(isAvailable).isFalse()
         }
 
     @Test
     fun widget_tutorialCompletedAndWidgetsAvailable_showWidgetContent() =
-        testScope.runTest {
+        kosmos.runTest {
             // Keyguard showing, and tutorial completed.
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            fakeKeyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setKeyguardOccluded(false)
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
-            userRepository.setUserInfos(userInfos)
-            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
-            runCurrent()
+            fakeUserRepository.setUserInfos(userInfos)
+            fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
 
             // Widgets available.
-            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
-            widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
-            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
             val widgetContent by collectLastValue(underTest.widgetContent)
 
@@ -356,18 +308,18 @@
         totalTargets: Int,
         expectedSizes: List<CommunalContentSize>,
     ) =
-        testScope.runTest {
+        kosmos.runTest {
             // Keyguard showing, and tutorial completed.
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            fakeKeyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setKeyguardOccluded(false)
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             val targets = mutableListOf<CommunalSmartspaceTimer>()
             for (index in 0 until totalTargets) {
                 targets.add(smartspaceTimer(index.toString()))
             }
 
-            smartspaceRepository.setTimers(targets)
+            fakeCommunalSmartspaceRepository.setTimers(targets)
 
             val smartspaceContent by collectLastValue(underTest.ongoingContent(false))
             assertThat(smartspaceContent?.size).isEqualTo(totalTargets)
@@ -378,12 +330,12 @@
 
     @Test
     fun umo_mediaPlaying_showsUmo() =
-        testScope.runTest {
+        kosmos.runTest {
             // Tutorial completed.
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             // Media is playing.
-            mediaRepository.mediaActive()
+            fakeCommunalMediaRepository.mediaActive()
 
             val umoContent by collectLastValue(underTest.ongoingContent(true))
 
@@ -394,12 +346,12 @@
 
     @Test
     fun umo_mediaPlaying_mediaHostNotVisible_hidesUmo() =
-        testScope.runTest {
+        kosmos.runTest {
             // Tutorial completed.
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             // Media is playing.
-            mediaRepository.mediaActive()
+            fakeCommunalMediaRepository.mediaActive()
 
             val umoContent by collectLastValue(underTest.ongoingContent(false))
             assertThat(umoContent?.size).isEqualTo(0)
@@ -409,26 +361,26 @@
     @Test
     @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
     fun ongoing_shouldOrderAndSizeByTimestamp() =
-        testScope.runTest {
+        kosmos.runTest {
             // Keyguard showing, and tutorial completed.
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            fakeKeyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setKeyguardOccluded(false)
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             // Timer1 started
             val timer1 = smartspaceTimer("timer1", timestamp = 1L)
-            smartspaceRepository.setTimers(listOf(timer1))
+            fakeCommunalSmartspaceRepository.setTimers(listOf(timer1))
 
             // Umo started
-            mediaRepository.mediaActive(timestamp = 2L)
+            fakeCommunalMediaRepository.mediaActive(timestamp = 2L)
 
             // Timer2 started
             val timer2 = smartspaceTimer("timer2", timestamp = 3L)
-            smartspaceRepository.setTimers(listOf(timer1, timer2))
+            fakeCommunalSmartspaceRepository.setTimers(listOf(timer1, timer2))
 
             // Timer3 started
             val timer3 = smartspaceTimer("timer3", timestamp = 4L)
-            smartspaceRepository.setTimers(listOf(timer1, timer2, timer3))
+            fakeCommunalSmartspaceRepository.setTimers(listOf(timer1, timer2, timer3))
 
             val ongoingContent by collectLastValue(underTest.ongoingContent(true))
             assertThat(ongoingContent?.size).isEqualTo(4)
@@ -447,8 +399,8 @@
 
     @Test
     fun ctaTile_showsByDefault() =
-        testScope.runTest {
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+        kosmos.runTest {
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             val ctaTileContent by collectLastValue(underTest.ctaTileContent)
 
@@ -461,14 +413,13 @@
 
     @Test
     fun ctaTile_afterDismiss_doesNotShow() =
-        testScope.runTest {
+        kosmos.runTest {
             // Set to main user, so we can dismiss the tile for the main user.
-            val user = userRepository.asMainUser()
-            userTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
-            runCurrent()
+            val user = fakeUserRepository.asMainUser()
+            fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
 
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-            communalPrefsRepository.setCtaDismissed(user)
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            fakeCommunalPrefsRepository.setCtaDismissed(user)
 
             val ctaTileContent by collectLastValue(underTest.ctaTileContent)
 
@@ -477,36 +428,30 @@
 
     @Test
     fun listensToSceneChange() =
-        testScope.runTest {
+        kosmos.runTest {
             kosmos.setCommunalAvailable(true)
-            runCurrent()
 
-            var desiredScene = collectLastValue(underTest.desiredScene)
-            runCurrent()
-            assertThat(desiredScene()).isEqualTo(CommunalScenes.Blank)
+            val desiredScene by collectLastValue(underTest.desiredScene)
+            assertThat(desiredScene).isEqualTo(CommunalScenes.Blank)
 
             val targetScene = CommunalScenes.Communal
-            communalRepository.changeScene(targetScene)
-            desiredScene = collectLastValue(underTest.desiredScene)
-            runCurrent()
-            assertThat(desiredScene()).isEqualTo(targetScene)
+            fakeCommunalSceneRepository.changeScene(targetScene)
+            assertThat(desiredScene).isEqualTo(targetScene)
         }
 
     @Test
     fun updatesScene() =
-        testScope.runTest {
+        kosmos.runTest {
             val targetScene = CommunalScenes.Communal
-
             underTest.changeScene(targetScene, "test")
 
-            val desiredScene = collectLastValue(communalRepository.currentScene)
-            runCurrent()
-            assertThat(desiredScene()).isEqualTo(targetScene)
+            val desiredScene by collectLastValue(fakeCommunalSceneRepository.currentScene)
+            assertThat(desiredScene).isEqualTo(targetScene)
         }
 
     @Test
     fun transitionProgress_onTargetScene_fullProgress() =
-        testScope.runTest {
+        kosmos.runTest {
             val targetScene = CommunalScenes.Blank
             val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
             val transitionProgress by collectLastValue(transitionProgressFlow)
@@ -524,7 +469,7 @@
 
     @Test
     fun transitionProgress_notOnTargetScene_noProgress() =
-        testScope.runTest {
+        kosmos.runTest {
             val targetScene = CommunalScenes.Blank
             val currentScene = CommunalScenes.Communal
             val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
@@ -543,7 +488,7 @@
 
     @Test
     fun transitionProgress_transitioningToTrackedScene() =
-        testScope.runTest {
+        kosmos.runTest {
             val currentScene = CommunalScenes.Communal
             val targetScene = CommunalScenes.Blank
             val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
@@ -591,7 +536,7 @@
 
     @Test
     fun transitionProgress_transitioningAwayFromTrackedScene() =
-        testScope.runTest {
+        kosmos.runTest {
             val currentScene = CommunalScenes.Blank
             val targetScene = CommunalScenes.Communal
             val transitionProgressFlow = underTest.transitionProgressToScene(currentScene)
@@ -642,52 +587,42 @@
 
     @Test
     fun isCommunalShowing() =
-        testScope.runTest {
+        kosmos.runTest {
             kosmos.setCommunalAvailable(true)
-            runCurrent()
 
-            var isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
-            runCurrent()
-            assertThat(isCommunalShowing()).isEqualTo(false)
+            val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
+            assertThat(isCommunalShowing).isEqualTo(false)
 
             underTest.changeScene(CommunalScenes.Communal, "test")
-
-            isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
-            runCurrent()
-            assertThat(isCommunalShowing()).isEqualTo(true)
+            assertThat(isCommunalShowing).isEqualTo(true)
         }
 
     @Test
     fun isCommunalShowing_whenSceneContainerDisabled() =
-        testScope.runTest {
+        kosmos.runTest {
             kosmos.setCommunalAvailable(true)
-            runCurrent()
 
             // Verify default is false
             val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
-            runCurrent()
             assertThat(isCommunalShowing).isFalse()
 
             // Verify scene changes with the flag doesn't have any impact
             sceneInteractor.changeScene(Scenes.Communal, loggingReason = "")
-            runCurrent()
             assertThat(isCommunalShowing).isFalse()
 
             // Verify scene changes (without the flag) to communal sets the value to true
             underTest.changeScene(CommunalScenes.Communal, "test")
-            runCurrent()
             assertThat(isCommunalShowing).isTrue()
 
             // Verify scene changes (without the flag) to blank sets the value back to false
             underTest.changeScene(CommunalScenes.Blank, "test")
-            runCurrent()
             assertThat(isCommunalShowing).isFalse()
         }
 
     @Test
     @EnableSceneContainer
     fun isCommunalShowing_whenSceneContainerEnabled() =
-        testScope.runTest {
+        kosmos.runTest {
             // Verify default is false
             val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
             assertThat(isCommunalShowing).isFalse()
@@ -704,7 +639,7 @@
     @Test
     @EnableSceneContainer
     fun isCommunalShowing_whenSceneContainerEnabledAndChangeToLegacyScene() =
-        testScope.runTest {
+        kosmos.runTest {
             // Verify default is false
             val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
             assertThat(isCommunalShowing).isFalse()
@@ -720,21 +655,19 @@
 
     @Test
     fun isIdleOnCommunal() =
-        testScope.runTest {
+        kosmos.runTest {
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Idle(CommunalScenes.Blank)
                 )
-            communalRepository.setTransitionState(transitionState)
+            fakeCommunalSceneRepository.setTransitionState(transitionState)
 
             // isIdleOnCommunal is false when not on communal.
             val isIdleOnCommunal by collectLastValue(underTest.isIdleOnCommunal)
-            runCurrent()
             assertThat(isIdleOnCommunal).isEqualTo(false)
 
             // Transition to communal.
             transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)
-            runCurrent()
 
             // isIdleOnCommunal is now true since we're on communal.
             assertThat(isIdleOnCommunal).isEqualTo(true)
@@ -749,7 +682,6 @@
                     isInitiatedByUserInput = false,
                     isUserInputOngoing = flowOf(false),
                 )
-            runCurrent()
 
             // isIdleOnCommunal turns false as soon as transition away starts.
             assertThat(isIdleOnCommunal).isEqualTo(false)
@@ -757,12 +689,12 @@
 
     @Test
     fun isCommunalVisible() =
-        testScope.runTest {
+        kosmos.runTest {
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Idle(CommunalScenes.Blank)
                 )
-            communalRepository.setTransitionState(transitionState)
+            fakeCommunalSceneRepository.setTransitionState(transitionState)
 
             // isCommunalVisible is false when not on communal.
             val isCommunalVisible by collectLastValue(underTest.isCommunalVisible)
@@ -805,7 +737,7 @@
 
     @Test
     fun testShowWidgetEditorStartsActivity() =
-        testScope.runTest {
+        kosmos.runTest {
             val editModeState by collectLastValue(communalSceneInteractor.editModeState)
 
             underTest.showWidgetEditor()
@@ -816,14 +748,14 @@
 
     @Test
     fun showWidgetEditor_openWidgetPickerOnStart_startsActivity() =
-        testScope.runTest {
+        kosmos.runTest {
             underTest.showWidgetEditor(shouldOpenWidgetPickerOnStart = true)
             verify(editWidgetsActivityStarter).startActivity(shouldOpenWidgetPickerOnStart = true)
         }
 
     @Test
     fun navigateToCommunalWidgetSettings_startsActivity() =
-        testScope.runTest {
+        kosmos.runTest {
             underTest.navigateToCommunalWidgetSettings()
             val intentCaptor = argumentCaptor<Intent>()
             verify(activityStarter)
@@ -833,23 +765,22 @@
 
     @Test
     fun filterWidgets_whenUserProfileRemoved() =
-        testScope.runTest {
+        kosmos.runTest {
             // Keyguard showing, and tutorial completed.
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            fakeKeyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setKeyguardOccluded(false)
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             // Only main user exists.
             val userInfos = listOf(MAIN_USER_INFO)
-            userRepository.setUserInfos(userInfos)
-            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
-            runCurrent()
+            fakeUserRepository.setUserInfos(userInfos)
+            fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
 
             val widgetContent by collectLastValue(underTest.widgetContent)
             // Given three widgets, and one of them is associated with pre-existing work profile.
-            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
-            widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
-            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
             // One widget is filtered out and the remaining two link to main user id.
             assertThat(checkNotNull(widgetContent).size).isEqualTo(2)
@@ -867,17 +798,16 @@
 
     @Test
     fun widgetContent_inQuietMode() =
-        testScope.runTest {
+        kosmos.runTest {
             // Keyguard showing, and tutorial completed.
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            fakeKeyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setKeyguardOccluded(false)
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             // Work profile is set up.
             val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
-            userRepository.setUserInfos(userInfos)
-            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
-            runCurrent()
+            fakeUserRepository.setUserInfos(userInfos)
+            fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
 
             // When work profile is paused.
             whenever(userManager.isQuietModeEnabled(eq(UserHandle.of(USER_INFO_WORK.id))))
@@ -885,9 +815,9 @@
             whenever(userManager.isManagedProfile(eq(USER_INFO_WORK.id))).thenReturn(true)
 
             val widgetContent by collectLastValue(underTest.widgetContent)
-            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
-            widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
-            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
             // The work profile widget is in quiet mode, while other widgets are not.
             assertThat(widgetContent).hasSize(3)
@@ -911,23 +841,25 @@
 
     @Test
     fun filterWidgets_whenDisallowedByDevicePolicyForWorkProfile() =
-        testScope.runTest {
+        kosmos.runTest {
             // Keyguard showing, and tutorial completed.
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            fakeKeyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setKeyguardOccluded(false)
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
-            userRepository.setUserInfos(userInfos)
-            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
-            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
-            runCurrent()
+            fakeUserRepository.setUserInfos(userInfos)
+            fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+            fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
 
             val widgetContent by collectLastValue(underTest.widgetContent)
             // One available work widget, one pending work widget, and one regular available widget.
-            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
-            widgetRepository.addPendingWidget(appWidgetId = 2, userId = USER_INFO_WORK.id)
-            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+            fakeCommunalWidgetRepository.addPendingWidget(
+                appWidgetId = 2,
+                userId = USER_INFO_WORK.id,
+            )
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
             setKeyguardFeaturesDisabled(
                 USER_INFO_WORK,
@@ -941,23 +873,25 @@
 
     @Test
     fun filterWidgets_whenAllowedByDevicePolicyForWorkProfile() =
-        testScope.runTest {
+        kosmos.runTest {
             // Keyguard showing, and tutorial completed.
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            fakeKeyguardRepository.setKeyguardShowing(true)
+            fakeKeyguardRepository.setKeyguardOccluded(false)
+            fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
-            userRepository.setUserInfos(userInfos)
-            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
-            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
-            runCurrent()
+            fakeUserRepository.setUserInfos(userInfos)
+            fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+            fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
 
             val widgetContent by collectLastValue(underTest.widgetContent)
             // Given three widgets, and one of them is associated with work profile.
-            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
-            widgetRepository.addPendingWidget(appWidgetId = 2, userId = USER_INFO_WORK.id)
-            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+            fakeCommunalWidgetRepository.addPendingWidget(
+                appWidgetId = 2,
+                userId = USER_INFO_WORK.id,
+            )
+            fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
             setKeyguardFeaturesDisabled(
                 USER_INFO_WORK,
@@ -973,7 +907,7 @@
 
     @Test
     fun showCommunalFromOccluded_enteredOccludedFromHub() =
-        testScope.runTest {
+        kosmos.runTest {
             kosmos.setCommunalAvailable(true)
             val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
             assertThat(showCommunalFromOccluded).isFalse()
@@ -989,7 +923,7 @@
 
     @Test
     fun showCommunalFromOccluded_enteredOccludedFromLockscreen() =
-        testScope.runTest {
+        kosmos.runTest {
             kosmos.setCommunalAvailable(true)
             val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
             assertThat(showCommunalFromOccluded).isFalse()
@@ -1005,7 +939,7 @@
 
     @Test
     fun showCommunalFromOccluded_communalBecomesUnavailableWhileOccluded() =
-        testScope.runTest {
+        kosmos.runTest {
             kosmos.setCommunalAvailable(true)
             val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
             assertThat(showCommunalFromOccluded).isFalse()
@@ -1015,7 +949,6 @@
                 to = KeyguardState.OCCLUDED,
                 testScope,
             )
-            runCurrent()
             kosmos.setCommunalAvailable(false)
 
             assertThat(showCommunalFromOccluded).isFalse()
@@ -1023,7 +956,7 @@
 
     @Test
     fun showCommunalFromOccluded_showBouncerWhileOccluded() =
-        testScope.runTest {
+        kosmos.runTest {
             kosmos.setCommunalAvailable(true)
             val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
             assertThat(showCommunalFromOccluded).isFalse()
@@ -1033,7 +966,6 @@
                 to = KeyguardState.OCCLUDED,
                 testScope,
             )
-            runCurrent()
             kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.OCCLUDED,
                 to = KeyguardState.PRIMARY_BOUNCER,
@@ -1045,7 +977,7 @@
 
     @Test
     fun showCommunalFromOccluded_enteredOccludedFromDreaming() =
-        testScope.runTest {
+        kosmos.runTest {
             kosmos.setCommunalAvailable(true)
             val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
             assertThat(showCommunalFromOccluded).isFalse()
@@ -1069,7 +1001,7 @@
 
     @Test
     fun dismissDisclaimerSetsDismissedFlag() =
-        testScope.runTest {
+        kosmos.runTest {
             val disclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed)
             assertThat(disclaimerDismissed).isFalse()
             underTest.setDisclaimerDismissed()
@@ -1078,17 +1010,17 @@
 
     @Test
     fun dismissDisclaimerTimeoutResetsDismissedFlag() =
-        testScope.runTest {
+        kosmos.runTest {
             val disclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed)
             underTest.setDisclaimerDismissed()
             assertThat(disclaimerDismissed).isTrue()
-            advanceTimeBy(CommunalInteractor.DISCLAIMER_RESET_MILLIS)
+            testScope.advanceTimeBy(CommunalInteractor.DISCLAIMER_RESET_MILLIS)
             assertThat(disclaimerDismissed).isFalse()
         }
 
     @Test
     fun settingSelectedKey_flowUpdated() {
-        testScope.runTest {
+        kosmos.runTest {
             val key = "test"
             val selectedKey by collectLastValue(underTest.selectedKey)
             underTest.setSelectedKey(key)
@@ -1098,36 +1030,35 @@
 
     @Test
     fun unpauseWorkProfileEnablesWorkMode() =
-        testScope.runTest {
+        kosmos.runTest {
             underTest.unpauseWorkProfile()
 
-            assertThat(managedProfileController.isWorkModeEnabled()).isTrue()
+            assertThat(fakeManagedProfileController.isWorkModeEnabled()).isTrue()
         }
 
     @Test
     @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
     @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
     fun resizeWidget_withoutUpdatingOrder() =
-        testScope.runTest {
+        kosmos.runTest {
             val userInfos = listOf(MAIN_USER_INFO)
-            userRepository.setUserInfos(userInfos)
-            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
-            runCurrent()
+            fakeUserRepository.setUserInfos(userInfos)
+            fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
 
             // Widgets available.
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 1,
                 userId = MAIN_USER_INFO.id,
                 rank = 0,
                 spanY = CommunalContentSize.FixedSize.HALF.span,
             )
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 2,
                 userId = MAIN_USER_INFO.id,
                 rank = 1,
                 spanY = CommunalContentSize.FixedSize.HALF.span,
             )
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 3,
                 userId = MAIN_USER_INFO.id,
                 rank = 2,
@@ -1159,26 +1090,25 @@
     @Test
     @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID)
     fun resizeWidget_withoutUpdatingOrder_responsive() =
-        testScope.runTest {
+        kosmos.runTest {
             val userInfos = listOf(MAIN_USER_INFO)
-            userRepository.setUserInfos(userInfos)
-            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
-            runCurrent()
+            fakeUserRepository.setUserInfos(userInfos)
+            fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
 
             // Widgets available.
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 1,
                 userId = MAIN_USER_INFO.id,
                 rank = 0,
                 spanY = 1,
             )
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 2,
                 userId = MAIN_USER_INFO.id,
                 rank = 1,
                 spanY = 1,
             )
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 3,
                 userId = MAIN_USER_INFO.id,
                 rank = 2,
@@ -1211,26 +1141,25 @@
     @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
     @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
     fun resizeWidget_andUpdateOrder() =
-        testScope.runTest {
+        kosmos.runTest {
             val userInfos = listOf(MAIN_USER_INFO)
-            userRepository.setUserInfos(userInfos)
-            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
-            runCurrent()
+            fakeUserRepository.setUserInfos(userInfos)
+            fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
 
             // Widgets available.
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 1,
                 userId = MAIN_USER_INFO.id,
                 rank = 0,
                 spanY = CommunalContentSize.FixedSize.HALF.span,
             )
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 2,
                 userId = MAIN_USER_INFO.id,
                 rank = 1,
                 spanY = CommunalContentSize.FixedSize.HALF.span,
             )
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 3,
                 userId = MAIN_USER_INFO.id,
                 rank = 2,
@@ -1266,26 +1195,25 @@
     @Test
     @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID)
     fun resizeWidget_andUpdateOrder_responsive() =
-        testScope.runTest {
+        kosmos.runTest {
             val userInfos = listOf(MAIN_USER_INFO)
-            userRepository.setUserInfos(userInfos)
-            userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
-            runCurrent()
+            fakeUserRepository.setUserInfos(userInfos)
+            fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
 
             // Widgets available.
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 1,
                 userId = MAIN_USER_INFO.id,
                 rank = 0,
                 spanY = 1,
             )
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 2,
                 userId = MAIN_USER_INFO.id,
                 rank = 1,
                 spanY = 1,
             )
-            widgetRepository.addWidget(
+            fakeCommunalWidgetRepository.addWidget(
                 appWidgetId = 3,
                 userId = MAIN_USER_INFO.id,
                 rank = 2,
@@ -1318,6 +1246,66 @@
                 .inOrder()
         }
 
+    @Test
+    fun showCommunalWhileCharging() =
+        kosmos.runTest {
+            fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+            fakeUserRepository.setSelectedUserInfo(mainUser)
+            fakeKeyguardRepository.setKeyguardShowing(true)
+            fakeSettings.putIntForUser(
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+                1,
+                mainUser.id,
+            )
+
+            val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
+            batteryRepository.fake.setDevicePluggedIn(false)
+            assertThat(shouldShowCommunal).isFalse()
+
+            batteryRepository.fake.setDevicePluggedIn(true)
+            assertThat(shouldShowCommunal).isTrue()
+        }
+
+    @Test
+    fun showCommunalWhilePosturedAndCharging() =
+        kosmos.runTest {
+            fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+            fakeUserRepository.setSelectedUserInfo(mainUser)
+            fakeKeyguardRepository.setKeyguardShowing(true)
+            fakeSettings.putIntForUser(
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+                1,
+                mainUser.id,
+            )
+
+            val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
+            batteryRepository.fake.setDevicePluggedIn(true)
+            posturingRepository.fake.setPosturedState(PosturedState.NotPostured)
+            assertThat(shouldShowCommunal).isFalse()
+
+            posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+            assertThat(shouldShowCommunal).isTrue()
+        }
+
+    @Test
+    fun showCommunalWhileDocked() =
+        kosmos.runTest {
+            fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+            fakeUserRepository.setSelectedUserInfo(mainUser)
+            fakeKeyguardRepository.setKeyguardShowing(true)
+            fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, 1, mainUser.id)
+
+            batteryRepository.fake.setDevicePluggedIn(true)
+            fakeDockManager.setIsDocked(false)
+
+            val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
+            assertThat(shouldShowCommunal).isFalse()
+
+            fakeDockManager.setIsDocked(true)
+            fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
+            assertThat(shouldShowCommunal).isTrue()
+        }
+
     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/communal/domain/interactor/CommunalPrefsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
index 1fef693..1f5f8ce 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
@@ -108,6 +108,43 @@
             assertThat(isHubOnboardingDismissed).isFalse()
         }
 
+    @Test
+    fun setDreamButtonTooltipDismissed_currentUser() =
+        testScope.runTest {
+            setSelectedUser(MAIN_USER)
+            val isDreamButtonTooltipDismissed by
+                collectLastValue(underTest.isDreamButtonTooltipDismissed)
+
+            assertThat(isDreamButtonTooltipDismissed).isFalse()
+            underTest.setDreamButtonTooltipDismissed(MAIN_USER)
+            assertThat(isDreamButtonTooltipDismissed).isTrue()
+        }
+
+    @Test
+    fun setDreamButtonTooltipDismissed_anotherUser() =
+        testScope.runTest {
+            setSelectedUser(MAIN_USER)
+            val isDreamButtonTooltipDismissed by
+                collectLastValue(underTest.isDreamButtonTooltipDismissed)
+
+            assertThat(isDreamButtonTooltipDismissed).isFalse()
+            underTest.setDreamButtonTooltipDismissed(SECONDARY_USER)
+            assertThat(isDreamButtonTooltipDismissed).isFalse()
+        }
+
+    @Test
+    fun isDreamButtonTooltipDismissed_userSwitch() =
+        testScope.runTest {
+            setSelectedUser(MAIN_USER)
+            underTest.setDreamButtonTooltipDismissed(MAIN_USER)
+            val isDreamButtonTooltipDismissed by
+                collectLastValue(underTest.isDreamButtonTooltipDismissed)
+
+            assertThat(isDreamButtonTooltipDismissed).isTrue()
+            setSelectedUser(SECONDARY_USER)
+            assertThat(isDreamButtonTooltipDismissed).isFalse()
+        }
+
     private suspend fun setSelectedUser(user: UserInfo) {
         with(kosmos.fakeUserRepository) {
             setUserInfos(listOf(user))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
index e4916b1..310bf64 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
@@ -21,19 +21,17 @@
 import android.content.Intent
 import android.content.pm.UserInfo
 import android.os.UserManager
-import android.os.userManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
-import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertNull
@@ -48,34 +46,20 @@
 @RunWith(AndroidJUnit4::class)
 class CommunalSettingsInteractorTest : SysuiTestCase() {
 
-    private lateinit var userManager: UserManager
-    private lateinit var userRepository: FakeUserRepository
-    private lateinit var userTracker: FakeUserTracker
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
 
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-
-    private lateinit var underTest: CommunalSettingsInteractor
+    private val Kosmos.underTest by Kosmos.Fixture { communalSettingsInteractor }
 
     @Before
     fun setUp() {
-        userManager = kosmos.userManager
-        userRepository = kosmos.fakeUserRepository
-        userTracker = kosmos.fakeUserTracker
-
         val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
-        userRepository.setUserInfos(userInfos)
-        userTracker.set(
-            userInfos = userInfos,
-            selectedUserIndex = 0,
-        )
-
-        underTest = kosmos.communalSettingsInteractor
+        kosmos.fakeUserRepository.setUserInfos(userInfos)
+        kosmos.fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
     }
 
     @Test
     fun filterUsers_dontFilteredUsersWhenAllAreAllowed() =
-        testScope.runTest {
+        kosmos.runTest {
             // If no users have any keyguard features disabled...
             val disallowedUser by
                 collectLastValue(underTest.workProfileUserDisallowedByDevicePolicy)
@@ -85,11 +69,11 @@
 
     @Test
     fun filterUsers_filterWorkProfileUserWhenDisallowed() =
-        testScope.runTest {
+        kosmos.runTest {
             // If the work profile user has keyguard widgets disabled...
             setKeyguardFeaturesDisabled(
                 USER_INFO_WORK,
-                DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+                DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL,
             )
             // ...then the disallowed user match the work profile
             val disallowedUser by
@@ -102,7 +86,7 @@
         whenever(
                 kosmos.devicePolicyManager.getKeyguardDisabledFeatures(
                     anyOrNull(),
-                    ArgumentMatchers.eq(user.id)
+                    ArgumentMatchers.eq(user.id),
                 )
             )
             .thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
index 012ae8f..b747705 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal.ui.viewmodel
 
+import android.content.pm.UserInfo
 import android.platform.test.annotations.EnableFlags
 import android.provider.Settings
 import android.service.dream.dreamManager
@@ -24,15 +25,17 @@
 import com.android.internal.logging.uiEventLoggerFake
 import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
+import com.android.systemui.communal.domain.interactor.HubOnboardingInteractorTest.Companion.MAIN_USER
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
 import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runCurrent
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.plugins.activityStarter
+import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.statusbar.policy.batteryController
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
@@ -52,7 +55,6 @@
 class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val uiEventLoggerFake = kosmos.uiEventLoggerFake
     private val underTest: CommunalToDreamButtonViewModel by lazy {
         kosmos.communalToDreamButtonViewModel
     }
@@ -68,9 +70,9 @@
         with(kosmos) {
             runTest {
                 whenever(batteryController.isPluggedIn()).thenReturn(true)
+                runCurrent()
 
-                val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
-                assertThat(shouldShowButton).isTrue()
+                assertThat(underTest.shouldShowDreamButtonOnHub).isTrue()
             }
         }
 
@@ -79,9 +81,9 @@
         with(kosmos) {
             runTest {
                 whenever(batteryController.isPluggedIn()).thenReturn(false)
+                runCurrent()
 
-                val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
-                assertThat(shouldShowButton).isFalse()
+                assertThat(underTest.shouldShowDreamButtonOnHub).isFalse()
             }
         }
 
@@ -124,6 +126,23 @@
         }
 
     @Test
+    fun shouldShowDreamButtonTooltip_trueWhenNotDismissed() =
+        kosmos.runTest {
+            runCurrent()
+            assertThat(underTest.shouldShowTooltip).isTrue()
+        }
+
+    @Test
+    fun shouldShowDreamButtonTooltip_falseWhenDismissed() =
+        kosmos.runTest {
+            setSelectedUser(MAIN_USER)
+            fakeCommunalPrefsRepository.setDreamButtonTooltipDismissed(MAIN_USER)
+            runCurrent()
+
+            assertThat(underTest.shouldShowTooltip).isFalse()
+        }
+
+    @Test
     fun onShowDreamButtonTap_eventLogged() =
         with(kosmos) {
             runTest {
@@ -134,4 +153,12 @@
                     .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_SHOW_DREAM_BUTTON_TAP.id)
             }
         }
+
+    private suspend fun setSelectedUser(user: UserInfo) {
+        with(kosmos.fakeUserRepository) {
+            setUserInfos(listOf(user))
+            setSelectedUserInfo(user)
+        }
+        kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt
index 26859b6..5510710 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt
@@ -18,8 +18,6 @@
 
 package com.android.systemui.communal.ui.viewmodel
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.Swipe
@@ -42,9 +40,9 @@
 import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.shade.data.repository.fakeShadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
-import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
+import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -72,33 +70,21 @@
     }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun actions_singleShade() =
         testScope.runTest {
             val actions by collectLastValue(underTest.actions)
+            kosmos.enableSingleShade()
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Single,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = false)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
 
-            setUpState(
-                isShadeTouchable = false,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Single,
-            )
+            setUpState(isShadeTouchable = false, isDeviceUnlocked = false)
             assertThat(actions).isEmpty()
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = true,
-                shadeMode = ShadeMode.Single,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = true)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
@@ -106,34 +92,22 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun actions_splitShade() =
         testScope.runTest {
             val actions by collectLastValue(underTest.actions)
+            kosmos.enableSplitShade()
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Split,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = false)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
                 .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
 
-            setUpState(
-                isShadeTouchable = false,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Split,
-            )
+            setUpState(isShadeTouchable = false, isDeviceUnlocked = false)
             assertThat(actions).isEmpty()
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = true,
-                shadeMode = ShadeMode.Split,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = true)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
@@ -142,30 +116,22 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun actions_dualShade() =
         testScope.runTest {
             val actions by collectLastValue(underTest.actions)
+            kosmos.enableDualShade()
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Dual,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = false)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
                 .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
 
-            setUpState(
-                isShadeTouchable = false,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Dual,
-            )
+            setUpState(isShadeTouchable = false, isDeviceUnlocked = false)
             assertThat(actions).isEmpty()
 
-            setUpState(isShadeTouchable = true, isDeviceUnlocked = true, shadeMode = ShadeMode.Dual)
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = true)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
@@ -173,11 +139,7 @@
                 .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
         }
 
-    private fun TestScope.setUpState(
-        isShadeTouchable: Boolean,
-        isDeviceUnlocked: Boolean,
-        shadeMode: ShadeMode,
-    ) {
+    private fun TestScope.setUpState(isShadeTouchable: Boolean, isDeviceUnlocked: Boolean) {
         if (isShadeTouchable) {
             kosmos.powerInteractor.setAwakeForTest()
         } else {
@@ -189,10 +151,6 @@
         } else {
             lockDevice()
         }
-
-        if (shadeMode !is ShadeMode.Dual) {
-            kosmos.fakeShadeRepository.setShadeLayoutWide(shadeMode is ShadeMode.Split)
-        }
         runCurrent()
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalLockIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalLockIconViewModelTest.kt
new file mode 100644
index 0000000..c535831
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalLockIconViewModelTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2025 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.communal.view.viewmodel
+
+import android.platform.test.flag.junit.FlagsParameterization
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
+import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.AuthenticationResult
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.communal.ui.viewmodel.CommunalLockIconViewModel
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntrySourceInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.advanceTimeBy
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+class CommunalLockIconViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+    private val Kosmos.underTest by
+        Kosmos.Fixture {
+            CommunalLockIconViewModel(
+                context = context,
+                configurationInteractor = configurationInteractor,
+                deviceEntryInteractor = deviceEntryInteractor,
+                keyguardInteractor = keyguardInteractor,
+                keyguardViewController = { statusBarKeyguardViewManager },
+                deviceEntrySourceInteractor = deviceEntrySourceInteractor,
+                accessibilityInteractor = accessibilityInteractor,
+            )
+        }
+
+    @Test
+    fun isLongPressEnabled_unlocked() =
+        kosmos.runTest {
+            val isLongPressEnabled by collectLastValue(underTest.isLongPressEnabled)
+            setLockscreenDismissible()
+            assertThat(isLongPressEnabled).isTrue()
+        }
+
+    @Test
+    fun isLongPressEnabled_lock() =
+        kosmos.runTest {
+            val isLongPressEnabled by collectLastValue(underTest.isLongPressEnabled)
+            if (!SceneContainerFlag.isEnabled) {
+                fakeKeyguardRepository.setKeyguardDismissible(false)
+            }
+            assertThat(isLongPressEnabled).isFalse()
+        }
+
+    @Test
+    fun iconType_locked() =
+        kosmos.runTest {
+            val viewAttributes by collectLastValue(underTest.viewAttributes)
+            if (!SceneContainerFlag.isEnabled) {
+                fakeKeyguardRepository.setKeyguardDismissible(false)
+            }
+            assertThat(viewAttributes?.type).isEqualTo(DeviceEntryIconView.IconType.LOCK)
+        }
+
+    @Test
+    fun iconType_unlocked() =
+        kosmos.runTest {
+            val viewAttributes by collectLastValue(underTest.viewAttributes)
+            setLockscreenDismissible()
+            assertThat(viewAttributes?.type).isEqualTo(DeviceEntryIconView.IconType.UNLOCK)
+        }
+
+    private suspend fun Kosmos.setLockscreenDismissible() {
+        if (SceneContainerFlag.isEnabled) {
+            // Need to set up a collection for the authentication to be propagated.
+            backgroundScope.launch { kosmos.deviceUnlockedInteractor.deviceUnlockStatus.collect {} }
+            assertThat(
+                    kosmos.authenticationInteractor.authenticate(
+                        FakeAuthenticationRepository.DEFAULT_PIN
+                    )
+                )
+                .isEqualTo(AuthenticationResult.SUCCEEDED)
+        } else {
+            fakeKeyguardRepository.setKeyguardDismissible(true)
+        }
+        testScope.advanceTimeBy(
+            DeviceEntryIconViewModel.UNLOCKED_DELAY_MS * 2
+        ) // wait for unlocked delay
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
index 6c955bf..5fd480f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
@@ -176,14 +176,14 @@
         }
 
     @Test
-    fun nonPowerButtonFPS_coExFaceFailure_doNotVibrateError() =
+    fun nonPowerButtonFPS_coExFaceFailure_vibrateError() =
         testScope.runTest {
             val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
             enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
             enrollFace()
             runCurrent()
             faceFailure()
-            assertThat(playErrorHaptic).isNull()
+            assertThat(playErrorHaptic).isNotNull()
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
index e19ea36..e567062 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
@@ -18,8 +18,6 @@
 
 package com.android.systemui.dreams.ui.viewmodel
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.Swipe
@@ -42,9 +40,9 @@
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.shade.data.repository.fakeShadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
-import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
+import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -72,36 +70,24 @@
     }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun actions_communalNotAvailable_singleShade() =
         testScope.runTest {
+            kosmos.enableSingleShade()
             kosmos.setCommunalAvailable(false)
 
             val actions by collectLastValue(underTest.actions)
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Single,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = false)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
             assertThat(actions?.get(Swipe.Start)).isNull()
             assertThat(actions?.get(Swipe.End)).isNull()
 
-            setUpState(
-                isShadeTouchable = false,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Single,
-            )
+            setUpState(isShadeTouchable = false, isDeviceUnlocked = false)
             assertThat(actions).isEmpty()
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = true,
-                shadeMode = ShadeMode.Single,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = true)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
@@ -110,18 +96,14 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun actions_communalNotAvailable_splitShade() =
         testScope.runTest {
+            kosmos.enableSplitShade()
             kosmos.setCommunalAvailable(false)
 
             val actions by collectLastValue(underTest.actions)
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Split,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = false)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
@@ -129,18 +111,10 @@
             assertThat(actions?.get(Swipe.Start)).isNull()
             assertThat(actions?.get(Swipe.End)).isNull()
 
-            setUpState(
-                isShadeTouchable = false,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Split,
-            )
+            setUpState(isShadeTouchable = false, isDeviceUnlocked = false)
             assertThat(actions).isEmpty()
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = true,
-                shadeMode = ShadeMode.Split,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = true)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down))
@@ -150,18 +124,14 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun actions_communalNotAvailable_dualShade() =
         testScope.runTest {
+            kosmos.enableDualShade()
             kosmos.setCommunalAvailable(false)
 
             val actions by collectLastValue(underTest.actions)
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Dual,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = false)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
@@ -169,14 +139,10 @@
             assertThat(actions?.get(Swipe.Start)).isNull()
             assertThat(actions?.get(Swipe.End)).isNull()
 
-            setUpState(
-                isShadeTouchable = false,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Dual,
-            )
+            setUpState(isShadeTouchable = false, isDeviceUnlocked = false)
             assertThat(actions).isEmpty()
 
-            setUpState(isShadeTouchable = true, isDeviceUnlocked = true, shadeMode = ShadeMode.Dual)
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = true)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down))
@@ -186,36 +152,24 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun actions_communalAvailable_singleShade() =
         testScope.runTest {
+            kosmos.enableSingleShade()
             kosmos.setCommunalAvailable(true)
 
             val actions by collectLastValue(underTest.actions)
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Single,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = false)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
             assertThat(actions?.get(Swipe.End)).isNull()
 
-            setUpState(
-                isShadeTouchable = false,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Single,
-            )
+            setUpState(isShadeTouchable = false, isDeviceUnlocked = false)
             assertThat(actions).isEmpty()
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = true,
-                shadeMode = ShadeMode.Single,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = true)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
@@ -224,18 +178,14 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun actions_communalAvailable_splitShade() =
         testScope.runTest {
+            kosmos.enableSplitShade()
             kosmos.setCommunalAvailable(true)
 
             val actions by collectLastValue(underTest.actions)
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Split,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = false)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
@@ -243,18 +193,10 @@
             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
             assertThat(actions?.get(Swipe.End)).isNull()
 
-            setUpState(
-                isShadeTouchable = false,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Split,
-            )
+            setUpState(isShadeTouchable = false, isDeviceUnlocked = false)
             assertThat(actions).isEmpty()
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = true,
-                shadeMode = ShadeMode.Split,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = true)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down))
@@ -264,18 +206,14 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun actions_communalAvailable_dualShade() =
         testScope.runTest {
+            kosmos.enableDualShade()
             kosmos.setCommunalAvailable(true)
 
             val actions by collectLastValue(underTest.actions)
 
-            setUpState(
-                isShadeTouchable = true,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Dual,
-            )
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = false)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
@@ -283,14 +221,10 @@
             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
             assertThat(actions?.get(Swipe.End)).isNull()
 
-            setUpState(
-                isShadeTouchable = false,
-                isDeviceUnlocked = false,
-                shadeMode = ShadeMode.Dual,
-            )
+            setUpState(isShadeTouchable = false, isDeviceUnlocked = false)
             assertThat(actions).isEmpty()
 
-            setUpState(isShadeTouchable = true, isDeviceUnlocked = true, shadeMode = ShadeMode.Dual)
+            setUpState(isShadeTouchable = true, isDeviceUnlocked = true)
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down))
@@ -299,11 +233,7 @@
             assertThat(actions?.get(Swipe.End)).isNull()
         }
 
-    private fun TestScope.setUpState(
-        isShadeTouchable: Boolean,
-        isDeviceUnlocked: Boolean,
-        shadeMode: ShadeMode,
-    ) {
+    private fun TestScope.setUpState(isShadeTouchable: Boolean, isDeviceUnlocked: Boolean) {
         if (isShadeTouchable) {
             kosmos.powerInteractor.setAwakeForTest()
         } else {
@@ -315,10 +245,6 @@
         } else {
             lockDevice()
         }
-
-        if (shadeMode !is ShadeMode.Dual) {
-            kosmos.fakeShadeRepository.setShadeLayoutWide(shadeMode is ShadeMode.Split)
-        }
         runCurrent()
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
index 5e023a2..6899d23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
@@ -34,7 +34,7 @@
 import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
 import com.android.systemui.keyboard.data.repository.keyboardRepository
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
 import com.android.systemui.testKosmos
 import com.android.systemui.touchpad.data.repository.touchpadRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
@@ -68,7 +68,7 @@
     private val keyboardRepository = kosmos.keyboardRepository
     private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
     private val userRepository = kosmos.fakeUserRepository
-    private val overviewProxyService = kosmos.mockOverviewProxyService
+    private val launcherProxyService = kosmos.mockLauncherProxyService
 
     private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
     private val eduClock = kosmos.fakeEduClock
@@ -519,8 +519,8 @@
     }
 
     private fun updateContextualEduStats(isTrackpadGesture: Boolean, gestureType: GestureType) {
-        val listenerCaptor = argumentCaptor<OverviewProxyListener>()
-        verify(overviewProxyService).addCallback(listenerCaptor.capture())
+        val listenerCaptor = argumentCaptor<LauncherProxyListener>()
+        verify(launcherProxyService).addCallback(listenerCaptor.capture())
         listenerCaptor.firstValue.updateContextualEduStats(isTrackpadGesture, gestureType)
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index 692b9c6..dc393c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -32,7 +32,7 @@
 import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
 import com.android.systemui.keyboard.data.repository.keyboardRepository
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
 import com.android.systemui.testKosmos
 import com.android.systemui.touchpad.data.repository.touchpadRepository
 import com.google.common.truth.Truth.assertThat
@@ -58,7 +58,7 @@
     private val touchpadRepository = kosmos.touchpadRepository
     private val keyboardRepository = kosmos.keyboardRepository
     private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
-    private val overviewProxyService = kosmos.mockOverviewProxyService
+    private val launcherProxyService = kosmos.mockLauncherProxyService
 
     private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
     private val eduClock = kosmos.fakeEduClock
@@ -167,16 +167,16 @@
         keyboardRepository.setIsAnyKeyboardConnected(true)
     }
 
-    private fun getOverviewProxyListener(): OverviewProxyListener {
-        val listenerCaptor = argumentCaptor<OverviewProxyListener>()
-        verify(overviewProxyService).addCallback(listenerCaptor.capture())
+    private fun getLauncherProxyListener(): LauncherProxyListener {
+        val listenerCaptor = argumentCaptor<LauncherProxyListener>()
+        verify(launcherProxyService).addCallback(listenerCaptor.capture())
         return listenerCaptor.firstValue
     }
 
     private fun TestScope.triggerEducation(gestureType: GestureType) {
         // Increment max number of signal to try triggering education
         for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
-            val listener = getOverviewProxyListener()
+            val listener = getLauncherProxyListener()
             listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
         }
         runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
index 698fac1..4d81cb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.content.Context.INPUT_SERVICE
 import android.content.Intent
+import android.hardware.input.FakeInputManager
 import android.hardware.input.InputGestureData
 import android.hardware.input.InputManager
 import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
@@ -57,13 +58,14 @@
     private val secondaryUserContext: Context = mock()
     private var activeUserContext: Context = primaryUserContext
 
-    private val kosmos = testKosmos().also {
-        it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { activeUserContext })
-    }
+    private val kosmos =
+        testKosmos().also {
+            it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { activeUserContext })
+        }
 
     private val inputManager = kosmos.fakeInputManager.inputManager
     private val broadcastDispatcher = kosmos.broadcastDispatcher
-    private val inputManagerForSecondaryUser: InputManager = mock()
+    private val inputManagerForSecondaryUser: InputManager = FakeInputManager().inputManager
     private val testScope = kosmos.testScope
     private val testHelper = kosmos.shortcutHelperTestHelper
     private val customInputGesturesRepository = kosmos.customInputGesturesRepository
@@ -94,9 +96,10 @@
     fun customInputGestures_initialValueReturnsDataFromAPI() {
         testScope.runTest {
             val customInputGestures = listOf(allAppsInputGestureData)
-            whenever(
-                inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
-            ).then { return@then customInputGestures }
+            whenever(inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY))
+                .then {
+                    return@then customInputGestures
+                }
 
             val inputGestures by collectLastValue(customInputGesturesRepository.customInputGestures)
 
@@ -108,9 +111,10 @@
     fun customInputGestures_isUpdatedToMostRecentDataAfterNewGestureIsAdded() {
         testScope.runTest {
             var customInputGestures = listOf<InputGestureData>()
-            whenever(
-                inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
-            ).then { return@then customInputGestures }
+            whenever(inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY))
+                .then {
+                    return@then customInputGestures
+                }
             whenever(inputManager.addCustomInputGesture(any())).then { invocation ->
                 val inputGesture = invocation.getArgument<InputGestureData>(0)
                 customInputGestures = customInputGestures + inputGesture
@@ -129,10 +133,10 @@
     fun retrieveCustomInputGestures_retrievesMostRecentData() {
         testScope.runTest {
             var customInputGestures = listOf<InputGestureData>()
-            whenever(
-                inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
-            ).then { return@then customInputGestures }
-
+            whenever(inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY))
+                .then {
+                    return@then customInputGestures
+                }
 
             assertThat(customInputGesturesRepository.retrieveCustomInputGestures()).isEmpty()
 
@@ -143,24 +147,38 @@
         }
     }
 
+    @Test
+    fun getInputGestureByTrigger_returnsInputGestureFromInputManager() =
+        testScope.runTest {
+            inputManager.addCustomInputGesture(allAppsInputGestureData)
+
+            val inputGestureData =
+                customInputGesturesRepository.getInputGestureByTrigger(
+                    allAppsInputGestureData.trigger
+                )
+
+            assertThat(inputGestureData).isEqualTo(allAppsInputGestureData)
+        }
+
     private fun setCustomInputGesturesForPrimaryUser(vararg inputGesture: InputGestureData) {
-        whenever(
-            inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
-        ).thenReturn(inputGesture.toList())
+        whenever(inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY))
+            .thenReturn(inputGesture.toList())
     }
 
     private fun setCustomInputGesturesForSecondaryUser(vararg inputGesture: InputGestureData) {
         whenever(
-            inputManagerForSecondaryUser.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
-        ).thenReturn(inputGesture.toList())
+                inputManagerForSecondaryUser.getCustomInputGestures(
+                    /* filter= */ InputGestureData.Filter.KEY
+                )
+            )
+            .thenReturn(inputGesture.toList())
     }
 
     private fun switchToSecondaryUser() {
         activeUserContext = secondaryUserContext
         broadcastDispatcher.sendIntentToMatchingReceiversOnly(
             context,
-            Intent(Intent.ACTION_USER_SWITCHED)
+            Intent(Intent.ACTION_USER_SWITCHED),
         )
     }
-
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
index 4cfb26e..522572d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
@@ -24,6 +24,7 @@
 import android.hardware.input.InputGestureData.createKeyTrigger
 import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
 import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
 import android.hardware.input.fakeInputManager
 import android.platform.test.annotations.DisableFlags
@@ -336,28 +337,6 @@
         }
     }
 
-    private suspend fun customizeShortcut(
-        customizationRequest: ShortcutCustomizationRequestInfo,
-        keyCombination: KeyCombination? = null,
-    ): ShortcutCustomizationRequestResult {
-        repo.onCustomizationRequested(customizationRequest)
-        repo.updateUserKeyCombination(keyCombination)
-
-        return when (customizationRequest) {
-            is SingleShortcutCustomization.Add -> {
-                repo.confirmAndSetShortcutCurrentlyBeingCustomized()
-            }
-
-            is SingleShortcutCustomization.Delete -> {
-                repo.deleteShortcutCurrentlyBeingCustomized()
-            }
-
-            else -> {
-                ShortcutCustomizationRequestResult.ERROR_OTHER
-            }
-        }
-    }
-
     @Test
     @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     fun categories_isUpdatedAfterCustomShortcutsAreReset() {
@@ -387,10 +366,66 @@
         }
     }
 
+    @Test
+    fun selectedKeyCombinationIsAvailable_whenTriggerIsNotRegisteredInInputManager() =
+        testScope.runTest {
+            helper.toggle(deviceId = 123)
+            repo.onCustomizationRequested(allAppsShortcutAddRequest)
+            repo.updateUserKeyCombination(standardKeyCombination)
+
+            assertThat(repo.isSelectedKeyCombinationAvailable()).isTrue()
+        }
+
+    @Test
+    fun selectedKeyCombinationIsNotAvailable_whenTriggerIsRegisteredInInputManager() =
+        testScope.runTest {
+            inputManager.addCustomInputGesture(buildInputGestureWithStandardKeyCombination())
+
+            helper.toggle(deviceId = 123)
+            repo.onCustomizationRequested(allAppsShortcutAddRequest)
+            repo.updateUserKeyCombination(standardKeyCombination)
+
+            assertThat(repo.isSelectedKeyCombinationAvailable()).isFalse()
+        }
+
     private fun setApiAppLaunchBookmarks(appLaunchBookmarks: List<InputGestureData>) {
         whenever(inputManager.appLaunchBookmarks).thenReturn(appLaunchBookmarks)
     }
 
+    private suspend fun customizeShortcut(
+        customizationRequest: ShortcutCustomizationRequestInfo,
+        keyCombination: KeyCombination? = null,
+    ): ShortcutCustomizationRequestResult {
+        repo.onCustomizationRequested(customizationRequest)
+        repo.updateUserKeyCombination(keyCombination)
+
+        return when (customizationRequest) {
+            is SingleShortcutCustomization.Add -> {
+                repo.confirmAndSetShortcutCurrentlyBeingCustomized()
+            }
+
+            is SingleShortcutCustomization.Delete -> {
+                repo.deleteShortcutCurrentlyBeingCustomized()
+            }
+
+            else -> {
+                ShortcutCustomizationRequestResult.ERROR_OTHER
+            }
+        }
+    }
+
+    private fun buildInputGestureWithStandardKeyCombination() =
+        InputGestureData.Builder()
+            .setKeyGestureType(KEY_GESTURE_TYPE_HOME)
+            .setTrigger(
+                createKeyTrigger(
+                    /* keycode= */ standardKeyCombination.keyCode!!,
+                    /* modifierState= */ standardKeyCombination.modifiers and
+                        ALL_SUPPORTED_MODIFIERS,
+                )
+            )
+            .build()
+
     private fun simpleInputGestureDataForAppLaunchShortcut(
         keyCode: Int = KEYCODE_A,
         modifiers: Int = META_CTRL_ON or META_ALT_ON,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
index 3bf59f3..cd05980 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
@@ -50,6 +50,7 @@
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
+import com.android.systemui.keyboard.shortcut.shortcutHelperAccessibilityShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperInputShortcutsSource
@@ -88,6 +89,7 @@
             it.shortcutHelperAppCategoriesShortcutsSource = fakeAppCategoriesSource
             it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource()
+            it.shortcutHelperAccessibilityShortcutsSource = FakeKeyboardShortcutGroupsSource()
         }
 
     private val repo = kosmos.defaultShortcutCategoriesRepository
@@ -284,14 +286,20 @@
             val categories by collectLastValue(repo.categories)
 
             val cycleForwardThroughRecentAppsShortcut =
-                categories?.first { it.type == ShortcutCategoryType.MultiTasking }
-                    ?.subCategories?.first { it.label == recentAppsGroup.label }
-                    ?.shortcuts?.first { it.label == CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
+                categories
+                    ?.first { it.type == ShortcutCategoryType.MultiTasking }
+                    ?.subCategories
+                    ?.first { it.label == recentAppsGroup.label }
+                    ?.shortcuts
+                    ?.first { it.label == CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
 
             val cycleBackThroughRecentAppsShortcut =
-                categories?.first { it.type == ShortcutCategoryType.MultiTasking }
-                    ?.subCategories?.first { it.label == recentAppsGroup.label }
-                    ?.shortcuts?.first { it.label == CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
+                categories
+                    ?.first { it.type == ShortcutCategoryType.MultiTasking }
+                    ?.subCategories
+                    ?.first { it.label == recentAppsGroup.label }
+                    ?.shortcuts
+                    ?.first { it.label == CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
 
             assertThat(cycleForwardThroughRecentAppsShortcut?.isCustomizable).isFalse()
             assertThat(cycleBackThroughRecentAppsShortcut?.isCustomizable).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
index 8f0bc64..6149098 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.InputMethodEditor
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
+import com.android.systemui.keyboard.shortcut.shortcutHelperAccessibilityShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesInteractor
 import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
@@ -76,6 +77,7 @@
             it.shortcutHelperMultiTaskingShortcutsSource = multitaskingShortcutsSource
             it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource()
+            it.shortcutHelperAccessibilityShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
index 000024f..7a34335 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
 import com.android.systemui.keyboard.shortcut.shortcutCustomizationDialogStarterFactory
+import com.android.systemui.keyboard.shortcut.shortcutHelperAccessibilityShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperInputShortcutsSource
@@ -66,6 +67,7 @@
             it.testDispatcher = UnconfinedTestDispatcher()
             it.shortcutHelperSystemShortcutsSource = fakeSystemSource
             it.shortcutHelperMultiTaskingShortcutsSource = fakeMultiTaskingSource
+            it.shortcutHelperAccessibilityShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
index d9d34f5..6eef5eb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
@@ -18,11 +18,15 @@
 
 import android.content.Context
 import android.content.Context.INPUT_SERVICE
-import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.createKeyTrigger
 import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER
 import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE
-import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME
 import android.hardware.input.fakeInputManager
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.META_CTRL_ON
+import android.view.KeyEvent.META_META_ON
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -30,7 +34,6 @@
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutAddRequest
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutDeleteRequest
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyDownEventWithActionKeyPressed
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyDownEventWithoutActionKeyPressed
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyUpEventWithActionKeyPressed
@@ -44,16 +47,17 @@
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.DeleteShortcutDialog
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.ResetShortcutDialog
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.userTracker
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.any
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
@@ -63,7 +67,7 @@
 
     private val mockUserContext: Context = mock()
     private val kosmos =
-        testKosmos().also {
+        testKosmos().useUnconfinedTestDispatcher().also {
             it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
         }
     private val testScope = kosmos.testScope
@@ -75,6 +79,7 @@
     fun setup() {
         helper.showFromActivity()
         whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
+        testScope.backgroundScope.launch { viewModel.activate() }
     }
 
     @Test
@@ -146,8 +151,6 @@
     fun uiState_becomeInactiveAfterSuccessfullySettingShortcut() {
         testScope.runTest {
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
-            whenever(inputManager.addCustomInputGesture(any()))
-                .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_SUCCESS)
 
             openAddShortcutDialogAndSetShortcut()
 
@@ -166,11 +169,38 @@
     }
 
     @Test
-    fun uiState_errorMessage_isKeyCombinationInUse_whenKeyCombinationAlreadyExists() {
+    fun uiState_errorMessage_onKeyPressed_isKeyCombinationInUse_whenKeyCombinationAlreadyExists() {
+        testScope.runTest {
+            inputManager.addCustomInputGesture(buildSimpleInputGestureWithMetaCtrlATrigger())
+            val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
+
+            openAddShortcutDialogAndPressKeyCombination()
+
+            assertThat((uiState as AddShortcutDialog).errorMessage)
+                .isEqualTo(
+                    context.getString(
+                        R.string.shortcut_customizer_key_combination_in_use_error_message
+                    )
+                )
+        }
+    }
+
+    @Test
+    fun uiState_errorMessage_onKeyPressed_isEmpty_whenKeyCombinationIsAvailable() {
         testScope.runTest {
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
-            whenever(inputManager.addCustomInputGesture(any()))
-                .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS)
+
+            openAddShortcutDialogAndPressKeyCombination()
+
+            assertThat((uiState as AddShortcutDialog).errorMessage).isEmpty()
+        }
+    }
+
+    @Test
+    fun uiState_errorMessage_onSetShortcut_isKeyCombinationInUse_whenKeyCombinationAlreadyExists() {
+        testScope.runTest {
+            inputManager.addCustomInputGesture(buildSimpleInputGestureWithMetaCtrlATrigger())
+            val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
 
             openAddShortcutDialogAndSetShortcut()
 
@@ -184,11 +214,12 @@
     }
 
     @Test
-    fun uiState_errorMessage_isKeyCombinationInUse_whenKeyCombinationIsReserved() {
+    fun uiState_errorMessage_onSetShortcut_isKeyCombinationInUse_whenKeyCombinationIsReserved() {
         testScope.runTest {
+            inputManager.addCustomInputGesture(buildSimpleInputGestureWithMetaCtrlATrigger())
+            kosmos.fakeInputManager.addCustomInputGestureErrorCode =
+                CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
-            whenever(inputManager.addCustomInputGesture(any()))
-                .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE)
 
             openAddShortcutDialogAndSetShortcut()
 
@@ -202,11 +233,12 @@
     }
 
     @Test
-    fun uiState_errorMessage_isGenericError_whenErrorIsUnknown() {
+    fun uiState_errorMessage_onSetShortcut_isGenericError_whenErrorIsUnknown() {
         testScope.runTest {
+            inputManager.addCustomInputGesture(buildSimpleInputGestureWithMetaCtrlATrigger())
+            kosmos.fakeInputManager.addCustomInputGestureErrorCode =
+                CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
-            whenever(inputManager.addCustomInputGesture(any()))
-                .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER)
 
             openAddShortcutDialogAndSetShortcut()
 
@@ -219,10 +251,7 @@
     fun uiState_becomesInactiveAfterSuccessfullyDeletingShortcut() {
         testScope.runTest {
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
-            whenever(inputManager.getCustomInputGestures(any()))
-                .thenReturn(listOf(goHomeInputGestureData, allAppsInputGestureData))
-            whenever(inputManager.removeCustomInputGesture(any()))
-                .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_SUCCESS)
+            inputManager.addCustomInputGesture(allAppsInputGestureData)
 
             openDeleteShortcutDialogAndDeleteShortcut()
 
@@ -234,7 +263,6 @@
     fun uiState_becomesInactiveAfterSuccessfullyResettingShortcuts() {
         testScope.runTest {
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
-            whenever(inputManager.getCustomInputGestures(any())).thenReturn(emptyList())
 
             openResetShortcutDialogAndResetAllCustomShortcuts()
 
@@ -297,24 +325,42 @@
         }
     }
 
-    private suspend fun openAddShortcutDialogAndSetShortcut() {
-        viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
+    @Test
+    fun uiState_pressedKeys_resetsToEmpty_onClearSelectedShortcutKeyCombination() {
+        testScope.runTest {
+            val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
+            viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
+            viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed)
+            viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed)
+            viewModel.clearSelectedKeyCombination()
+            assertThat((uiState as AddShortcutDialog).pressedKeys).isEmpty()
+        }
+    }
 
+    private suspend fun openAddShortcutDialogAndSetShortcut() {
+        openAddShortcutDialogAndPressKeyCombination()
+        viewModel.onSetShortcut()
+    }
+
+    private fun openAddShortcutDialogAndPressKeyCombination() {
+        viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
         viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed)
         viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed)
-
-        viewModel.onSetShortcut()
     }
 
     private suspend fun openDeleteShortcutDialogAndDeleteShortcut() {
         viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
-
         viewModel.deleteShortcutCurrentlyBeingCustomized()
     }
 
     private suspend fun openResetShortcutDialogAndResetAllCustomShortcuts() {
         viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
-
         viewModel.resetAllCustomShortcuts()
     }
+
+    private fun buildSimpleInputGestureWithMetaCtrlATrigger() =
+        InputGestureData.Builder()
+            .setKeyGestureType(KEY_GESTURE_TYPE_HOME)
+            .setTrigger(createKeyTrigger(KEYCODE_A, META_CTRL_ON or META_META_ON))
+            .build()
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 3fc46b9..cf38072 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
 import com.android.systemui.keyboard.shortcut.shared.model.shortcut
+import com.android.systemui.keyboard.shortcut.shortcutHelperAccessibilityShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperInputShortcutsSource
@@ -95,6 +96,7 @@
             it.shortcutHelperMultiTaskingShortcutsSource = fakeMultiTaskingSource
             it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource()
+            it.shortcutHelperAccessibilityShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.shortcutHelperCurrentAppShortcutsSource = fakeCurrentAppsSource
             it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
         }
@@ -112,9 +114,12 @@
         fakeSystemSource.setGroups(TestShortcuts.systemGroups)
         fakeMultiTaskingSource.setGroups(TestShortcuts.multitaskingGroups)
         fakeCurrentAppsSource.setGroups(TestShortcuts.currentAppGroups)
-        whenever(mockPackageManager.getApplicationInfo(anyString(), eq(0))).thenReturn(mockApplicationInfo)
-        whenever(mockPackageManager.getApplicationLabel(mockApplicationInfo)).thenReturn("Current App")
-        whenever(mockPackageManager.getApplicationIcon(anyString())).thenThrow(NameNotFoundException())
+        whenever(mockPackageManager.getApplicationInfo(anyString(), eq(0)))
+            .thenReturn(mockApplicationInfo)
+        whenever(mockPackageManager.getApplicationLabel(mockApplicationInfo))
+            .thenReturn("Current App")
+        whenever(mockPackageManager.getApplicationIcon(anyString()))
+            .thenThrow(NameNotFoundException())
         whenever(mockUserContext.packageManager).thenReturn(mockPackageManager)
         whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
     }
@@ -278,11 +283,11 @@
     fun shortcutsUiState_currentAppIsLauncher_defaultSelectedCategoryIsSystem() =
         testScope.runTest {
             whenever(
-                mockRoleManager.getRoleHoldersAsUser(
-                    RoleManager.ROLE_HOME,
-                    fakeUserTracker.userHandle,
+                    mockRoleManager.getRoleHoldersAsUser(
+                        RoleManager.ROLE_HOME,
+                        fakeUserTracker.userHandle,
+                    )
                 )
-            )
                 .thenReturn(listOf(TestShortcuts.currentAppPackageName))
             val uiState by collectLastValue(viewModel.shortcutsUiState)
 
@@ -318,23 +323,23 @@
                         label = "System",
                         iconSource = IconSource(imageVector = Icons.Default.Tv),
                         shortcutCategory =
-                        ShortcutCategory(
-                            System,
-                            subCategoryWithShortcutLabels("first Foo shortcut1"),
-                            subCategoryWithShortcutLabels(
-                                "second foO shortcut2",
-                                subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL,
+                            ShortcutCategory(
+                                System,
+                                subCategoryWithShortcutLabels("first Foo shortcut1"),
+                                subCategoryWithShortcutLabels(
+                                    "second foO shortcut2",
+                                    subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL,
+                                ),
                             ),
-                        ),
                     ),
                     ShortcutCategoryUi(
                         label = "Multitasking",
                         iconSource = IconSource(imageVector = Icons.Default.VerticalSplit),
                         shortcutCategory =
-                        ShortcutCategory(
-                            MultiTasking,
-                            subCategoryWithShortcutLabels("third FoO shortcut1"),
-                        ),
+                            ShortcutCategory(
+                                MultiTasking,
+                                subCategoryWithShortcutLabels("third FoO shortcut1"),
+                            ),
                     ),
                 )
         }
@@ -420,9 +425,8 @@
     @Test
     fun shortcutsUiState_shouldShowResetButton_isTrueWhenThereAreCustomShortcuts() =
         testScope.runTest {
-            whenever(
-                inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
-            ).thenReturn(listOf(allAppsInputGestureData))
+            whenever(inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY))
+                .thenReturn(listOf(allAppsInputGestureData))
             val uiState by collectLastValue(viewModel.shortcutsUiState)
 
             testHelper.showFromActivity()
@@ -433,7 +437,7 @@
 
     @Test
     fun shortcutsUiState_searchQuery_isResetAfterHelperIsClosedAndReOpened() =
-        testScope.runTest{
+        testScope.runTest {
             val uiState by collectLastValue(viewModel.shortcutsUiState)
 
             openHelperAndSearchForFooString()
@@ -443,7 +447,7 @@
             assertThat((uiState as? ShortcutsUiState.Active)?.searchQuery).isEqualTo("")
         }
 
-    private fun openHelperAndSearchForFooString(){
+    private fun openHelperAndSearchForFooString() {
         testHelper.showFromActivity()
         viewModel.onSearchQueryChanged("foo")
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index 9300964..3f2e78c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -39,6 +39,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -85,11 +87,13 @@
 
     private lateinit var powerInteractor: PowerInteractor
     private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
 
     @Before
     fun setup() {
         powerInteractor = kosmos.powerInteractor
         transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
+        bouncerRepository = kosmos.fakeKeyguardBouncerRepository
         underTest = kosmos.fromAodTransitionInteractor
 
         underTest.start()
@@ -304,6 +308,29 @@
         }
 
     @Test
+    fun testWakeAndUnlock_transitionsToGone_evenIfBouncerShows() =
+        testScope.runTest {
+            kosmos.fakeKeyguardRepository.setBiometricUnlockState(
+                BiometricUnlockMode.WAKE_AND_UNLOCK
+            )
+            runCurrent()
+            bouncerRepository.setPrimaryShow(true)
+            runCurrent()
+            powerInteractor.setAwakeForTest()
+            runCurrent()
+
+            // Waking up from wake and unlock should not start any transitions, we'll wait for the
+            // dismiss call.
+            assertThat(transitionRepository).noTransitionsStarted()
+
+            underTest.dismissAod()
+            advanceTimeBy(100) // account for debouncing
+
+            assertThat(transitionRepository)
+                .startedTransition(from = KeyguardState.AOD, to = KeyguardState.GONE)
+        }
+
+    @Test
     fun testTransitionToOccluded_onWake() =
         testScope.runTest {
             kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index c8a1648..abcbdb1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.Flags
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
+import com.android.systemui.Flags.glanceableHubV2
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.communal.domain.interactor.CommunalSceneTransitionInteractor
@@ -34,6 +36,8 @@
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSceneTransitionInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
+import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.DisableSceneContainer
@@ -153,6 +157,9 @@
         if (!SceneContainerFlag.isEnabled) {
             mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
         }
+        if (glanceableHubV2()) {
+            kosmos.setCommunalV2ConfigEnabled(true)
+        }
         featureFlags = FakeFeatureFlags()
 
         fromLockscreenTransitionInteractor.start()
@@ -1948,6 +1955,39 @@
 
     @Test
     @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_GLANCEABLE_HUB_V2)
+    fun glanceableHubToDreaming_v2() =
+        testScope.runTest {
+            kosmos.setCommunalV2Enabled(true)
+
+            // GIVEN a device that is not dreaming or dozing
+            keyguardRepository.setDreamingWithOverlay(false)
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            advanceTimeBy(600.milliseconds)
+
+            // GIVEN a prior transition has run to glanceable hub
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            keyguardRepository.setDreamingWithOverlay(true)
+            advanceTimeBy(100.milliseconds)
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.DREAMING,
+                    animatorAssertion = { it.isNull() },
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    @DisableSceneContainer
     @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToDreaming() =
         testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 9d5bf4d..5f5d80c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -794,6 +794,122 @@
                 TransitionStep(
                     transitionState = TransitionState.STARTED,
                     from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                )
+            )
+            runCurrent()
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.RUNNING,
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                )
+            )
+            runCurrent()
+
+            assertEquals(
+                listOf(
+                    true,
+                    // Still not visible during GONE -> LOCKSCREEN.
+                    false,
+                ),
+                values,
+            )
+
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.FINISHED,
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                )
+            )
+            runCurrent()
+
+            assertEquals(
+                listOf(
+                    true,
+                    false,
+                    // Visible now that we're FINISHED in LOCKSCREEN.
+                    true,
+                ),
+                values,
+            )
+
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.GONE,
+                )
+            )
+            runCurrent()
+
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.RUNNING,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.GONE,
+                )
+            )
+            runCurrent()
+
+            assertEquals(
+                listOf(
+                    true,
+                    false,
+                    // Remains true until the transition ends.
+                    true,
+                ),
+                values,
+            )
+
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.FINISHED,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.GONE,
+                )
+            )
+
+            runCurrent()
+            assertEquals(
+                listOf(
+                    true,
+                    false,
+                    true,
+                    // Until we're finished in GONE again.
+                    false,
+                ),
+                values,
+            )
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun testLockscreenVisibility_falseDuringWakeAndUnlockToGone_fromNotCanceledGone() =
+        testScope.runTest {
+            val values by collectValues(underTest.value.lockscreenVisibility)
+
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GONE,
+                testScope,
+            )
+
+            runCurrent()
+            assertEquals(
+                listOf(
+                    true,
+                    // Not visible when finished in GONE.
+                    false,
+                ),
+                values,
+            )
+
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.STARTED,
+                    from = KeyguardState.GONE,
                     to = KeyguardState.AOD,
                 )
             )
@@ -857,8 +973,9 @@
                 listOf(
                     true,
                     false,
-                    // Remains visible from AOD during transition.
                     true,
+                    // Becomes false immediately since we're wake and unlocking.
+                    false,
                 ),
                 values,
             )
@@ -1045,6 +1162,41 @@
             assertThat(usingKeyguardGoingAwayAnimation).isFalse()
         }
 
+    @Test
+    fun aodVisibility_visibleFullyInAod_falseOtherwise() =
+        testScope.runTest {
+            val aodVisibility by collectValues(underTest.value.aodVisibility)
+
+            transitionRepository.sendTransitionStepsThroughRunning(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                testScope,
+                throughValue = 0.5f,
+            )
+
+            assertEquals(listOf(false), aodVisibility)
+
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.FINISHED,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                )
+            )
+            runCurrent()
+
+            assertEquals(listOf(false, true), aodVisibility)
+
+            transitionRepository.sendTransitionStepsThroughRunning(
+                from = KeyguardState.AOD,
+                to = KeyguardState.GONE,
+                testScope,
+            )
+            runCurrent()
+
+            assertEquals(listOf(false, true, false), aodVisibility)
+        }
+
     companion object {
         private val progress = MutableStateFlow(0f)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
index 83b8216..e93ed39 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
@@ -20,6 +20,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_BOUNCER_UI_REVAMP
+import com.android.systemui.Flags.FLAG_NOTIFICATION_SHADE_BLUR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.flags.BrokenWithSceneContainer
@@ -97,6 +98,7 @@
         }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_SHADE_BLUR)
     fun blurRadiusGoesToMaximumWhenShadeIsExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.windowBlurRadius)
@@ -123,8 +125,8 @@
 
             kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
                 transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
-                startValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
-                endValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
+                startValue = kosmos.blurConfig.maxBlurRadiusPx,
+                endValue = kosmos.blurConfig.maxBlurRadiusPx,
                 transitionFactory = ::step,
                 actualValuesProvider = { values },
                 checkInterpolatedValues = false,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModelTest.kt
new file mode 100644
index 0000000..b4d546d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModelTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DozingToGlanceableHubTransitionViewModelTest : SysuiTestCase() {
+    val kosmos = testKosmos()
+    val testScope = kosmos.testScope
+
+    val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    val configurationRepository = kosmos.fakeConfigurationRepository
+    val underTest by lazy { kosmos.dozingToGlanceableHubTransitionViewModel }
+
+    @Test
+    @DisableSceneContainer
+    fun blurBecomesMaxValueImmediately() =
+        kosmos.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = blurConfig.minBlurRadiusPx,
+                endValue = blurConfig.maxBlurRadiusPx,
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.DOZING,
+                        to = KeyguardState.GLANCEABLE_HUB,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "DozingToGlanceableHubTransitionViewModelTest",
+                    )
+                },
+                checkInterpolatedValues = false,
+            )
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
index cd0a11c..cf86c25 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
@@ -23,16 +23,19 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
@@ -49,11 +52,11 @@
 
     @Test
     fun dreamOverlayAlpha() =
-        testScope.runTest {
+        kosmos.runTest {
             val values by collectValues(underTest.dreamOverlayAlpha)
             assertThat(values).isEmpty()
 
-            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+            fakeKeyguardTransitionRepository.sendTransitionSteps(
                 listOf(
                     // Should start running here...
                     step(0f, TransitionState.STARTED),
@@ -72,10 +75,10 @@
 
     @Test
     fun dreamOverlayTranslationX() =
-        testScope.runTest {
+        kosmos.runTest {
             configurationRepository.setDimensionPixelSize(
                 R.dimen.dreaming_to_hub_transition_dream_overlay_translation_x,
-                -100
+                -100,
             )
             val configuration: Configuration = mock()
             whenever(configuration.layoutDirection).thenReturn(LayoutDirection.LTR)
@@ -84,12 +87,8 @@
             val values by collectValues(underTest.dreamOverlayTranslationX)
             assertThat(values).isEmpty()
 
-            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
-                listOf(
-                    step(0f, TransitionState.STARTED),
-                    step(0.3f),
-                    step(0.6f),
-                ),
+            fakeKeyguardTransitionRepository.sendTransitionSteps(
+                listOf(step(0f, TransitionState.STARTED), step(0.3f), step(0.6f)),
                 testScope,
             )
 
@@ -97,16 +96,40 @@
             values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }
         }
 
+    @Test
+    @DisableSceneContainer
+    fun blurBecomesMaxValueImmediately() =
+        kosmos.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = blurConfig.minBlurRadiusPx,
+                endValue = blurConfig.maxBlurRadiusPx,
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.DREAMING,
+                        to = KeyguardState.GLANCEABLE_HUB,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "DreamingToGlanceableHubTransitionViewModelTest",
+                    )
+                },
+                checkInterpolatedValues = false,
+            )
+        }
+
     private fun step(
         value: Float,
-        state: TransitionState = TransitionState.RUNNING
+        state: TransitionState = TransitionState.RUNNING,
     ): TransitionStep {
         return TransitionStep(
             from = KeyguardState.DREAMING,
             to = KeyguardState.GLANCEABLE_HUB,
             value = value,
             transitionState = state,
-            ownerName = "DreamingToGlanceableHubTransitionViewModelTest"
+            ownerName = "DreamingToGlanceableHubTransitionViewModelTest",
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDozingTransitionViewModelTest.kt
new file mode 100644
index 0000000..eb01681
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDozingTransitionViewModelTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GlanceableHubToDozingTransitionViewModelTest : SysuiTestCase() {
+    val kosmos = testKosmos()
+    val testScope = kosmos.testScope
+
+    val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    val configurationRepository = kosmos.fakeConfigurationRepository
+    val underTest by lazy { kosmos.glanceableHubToDozingTransitionViewModel }
+
+    @Test
+    @DisableSceneContainer
+    fun blurBecomesMinValueImmediately() =
+        kosmos.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = kosmos.blurConfig.maxBlurRadiusPx,
+                endValue = kosmos.blurConfig.minBlurRadiusPx,
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.GLANCEABLE_HUB,
+                        to = KeyguardState.DOZING,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "GlanceableHubToDozingTransitionViewModelTest",
+                    )
+                },
+                checkInterpolatedValues = false,
+            )
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
index 69361ef..dd9563f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
@@ -22,17 +22,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
@@ -49,11 +51,11 @@
 
     @Test
     fun dreamOverlayAlpha() =
-        testScope.runTest {
+        kosmos.runTest {
             val values by collectValues(underTest.dreamOverlayAlpha)
             assertThat(values).isEmpty()
 
-            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+            fakeKeyguardTransitionRepository.sendTransitionSteps(
                 listOf(
                     step(0f, TransitionState.STARTED),
                     step(0f),
@@ -72,24 +74,20 @@
 
     @Test
     fun dreamOverlayTranslationX() =
-        testScope.runTest {
+        kosmos.runTest {
             val config: Configuration = mock()
             whenever(config.layoutDirection).thenReturn(LayoutDirection.LTR)
             configurationRepository.onConfigurationChange(config)
             configurationRepository.setDimensionPixelSize(
                 R.dimen.hub_to_dreaming_transition_dream_overlay_translation_x,
-                100
+                100,
             )
 
             val values by collectValues(underTest.dreamOverlayTranslationX)
             assertThat(values).isEmpty()
 
-            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
-                listOf(
-                    step(0f, TransitionState.STARTED),
-                    step(0.3f),
-                    step(0.6f),
-                ),
+            fakeKeyguardTransitionRepository.sendTransitionSteps(
+                listOf(step(0f, TransitionState.STARTED), step(0.3f), step(0.6f)),
                 testScope,
             )
 
@@ -97,16 +95,40 @@
             values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }
         }
 
+    @Test
+    @DisableSceneContainer
+    fun blurBecomesMinValueImmediately() =
+        kosmos.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = blurConfig.maxBlurRadiusPx,
+                endValue = blurConfig.minBlurRadiusPx,
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.GLANCEABLE_HUB,
+                        to = KeyguardState.DREAMING,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "GlanceableHubToDreamingTransitionViewModelTest",
+                    )
+                },
+                checkInterpolatedValues = false,
+            )
+        }
+
     private fun step(
         value: Float,
-        state: TransitionState = TransitionState.RUNNING
+        state: TransitionState = TransitionState.RUNNING,
     ): TransitionStep {
         return TransitionStep(
             from = KeyguardState.GLANCEABLE_HUB,
             to = KeyguardState.DREAMING,
             value = value,
             transitionState = state,
-            ownerName = GlanceableHubToDreamingTransitionViewModelTest::class.java.simpleName
+            ownerName = GlanceableHubToDreamingTransitionViewModelTest::class.java.simpleName,
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
index d2be649..1dd435b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
@@ -22,18 +22,20 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
@@ -52,7 +54,7 @@
 
     @Test
     fun lockscreenFadeIn() =
-        testScope.runTest {
+        kosmos.runTest {
             val values by collectValues(underTest.keyguardAlpha)
             assertThat(values).isEmpty()
 
@@ -80,14 +82,14 @@
 
     @Test
     fun lockscreenTranslationX() =
-        testScope.runTest {
+        kosmos.runTest {
             val config: Configuration = mock()
             whenever(config.layoutDirection).thenReturn(LayoutDirection.LTR)
             configurationRepository.onConfigurationChange(config)
 
             configurationRepository.setDimensionPixelSize(
                 R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
-                100
+                100,
             )
             val values by collectValues(underTest.keyguardTranslationX)
             assertThat(values).isEmpty()
@@ -109,14 +111,14 @@
 
     @Test
     fun lockscreenTranslationX_resetsAfterCancellation() =
-        testScope.runTest {
+        kosmos.runTest {
             val config: Configuration = mock()
             whenever(config.layoutDirection).thenReturn(LayoutDirection.LTR)
             configurationRepository.onConfigurationChange(config)
 
             configurationRepository.setDimensionPixelSize(
                 R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
-                100
+                100,
             )
             val values by collectValues(underTest.keyguardTranslationX)
             assertThat(values).isEmpty()
@@ -126,7 +128,7 @@
                     step(0f, TransitionState.STARTED),
                     step(0.3f),
                     step(0.5f),
-                    step(0.9f, TransitionState.CANCELED)
+                    step(0.9f, TransitionState.CANCELED),
                 ),
                 testScope,
             )
@@ -136,16 +138,40 @@
             assertThat(values.last().value).isEqualTo(0f)
         }
 
+    @Test
+    @DisableSceneContainer
+    fun blurBecomesMinValueImmediately() =
+        kosmos.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = blurConfig.maxBlurRadiusPx,
+                endValue = blurConfig.minBlurRadiusPx,
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.GLANCEABLE_HUB,
+                        to = KeyguardState.LOCKSCREEN,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "GlanceableHubToLockscreenTransitionViewModelTest",
+                    )
+                },
+                checkInterpolatedValues = false,
+            )
+        }
+
     private fun step(
         value: Float,
-        state: TransitionState = TransitionState.RUNNING
+        state: TransitionState = TransitionState.RUNNING,
     ): TransitionStep {
         return TransitionStep(
             from = KeyguardState.GLANCEABLE_HUB,
             to = KeyguardState.LOCKSCREEN,
             value = value,
             transitionState = state,
-            ownerName = this::class.java.simpleName
+            ownerName = this::class.java.simpleName,
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModelTest.kt
new file mode 100644
index 0000000..7eca17d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModelTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GlanceableHubToOccludedTransitionViewModelTest : SysuiTestCase() {
+    val kosmos = testKosmos()
+    val testScope = kosmos.testScope
+
+    val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
+    val underTest by lazy { kosmos.glanceableHubToOccludedTransitionViewModel }
+
+    @Test
+    @DisableSceneContainer
+    fun blurBecomesMinValueImmediately() =
+        kosmos.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = blurConfig.maxBlurRadiusPx,
+                endValue = blurConfig.minBlurRadiusPx,
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.GLANCEABLE_HUB,
+                        to = KeyguardState.OCCLUDED,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "GlanceableHubToOccludedTransitionViewModelTest",
+                    )
+                },
+                checkInterpolatedValues = false,
+            )
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelTest.kt
index abf8b39..54d20d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelTest.kt
@@ -16,18 +16,20 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectValues
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.transitions.blurConfig
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -36,19 +38,19 @@
 @RunWith(AndroidJUnit4::class)
 class GlanceableHubToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
     private val underTest by lazy { kosmos.glanceableHubToPrimaryBouncerTransitionViewModel }
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
     fun blurBecomesMaxValueImmediately() =
-        testScope.runTest {
+        kosmos.runTest {
             val values by collectValues(underTest.windowBlurRadius)
 
             kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
                 transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
-                startValue = kosmos.blurConfig.maxBlurRadiusPx,
-                endValue = kosmos.blurConfig.maxBlurRadiusPx,
+                startValue = blurConfig.maxBlurRadiusPx,
+                endValue = blurConfig.maxBlurRadiusPx,
                 actualValuesProvider = { values },
                 transitionFactory = { step, transitionState ->
                     TransitionStep(
@@ -62,4 +64,26 @@
                 checkInterpolatedValues = false,
             )
         }
+
+    @Test
+    @DisableSceneContainer
+    @EnableFlags(FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
+    fun noBlurTransitionWithBlurredGlanceableHub() =
+        kosmos.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            keyguardWindowBlurTestUtil.assertNoBlurRadiusTransition(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.GLANCEABLE_HUB,
+                        to = KeyguardState.PRIMARY_BOUNCER,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "GlanceableHubToPrimaryBouncerTransitionViewModelTest",
+                    )
+                },
+            )
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
index aab46d8..724d4c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
@@ -46,6 +47,7 @@
                 handler = kosmos.fakeExecutorHandler,
                 keyguardBlueprintInteractor = keyguardBlueprintInteractor,
                 keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
+                blueprintLog = logcatLogBuffer("blueprints"),
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt
new file mode 100644
index 0000000..38829da
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardMediaViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+    private val underTest = kosmos.keyguardMediaViewModelFactory.create()
+
+    @Before
+    fun setUp() {
+        underTest.activateIn(kosmos.testScope)
+    }
+
+    @Test
+    fun onDozing_noActiveMedia_mediaIsHidden() =
+        kosmos.runTest {
+            keyguardRepository.setIsDozing(true)
+
+            assertThat(underTest.isMediaVisible).isFalse()
+        }
+
+    @Test
+    fun onDozing_activeMediaExists_mediaIsHidden() =
+        kosmos.runTest {
+            val userMedia = MediaData(active = true)
+
+            mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+            keyguardRepository.setIsDozing(true)
+
+            assertThat(underTest.isMediaVisible).isFalse()
+        }
+
+    @Test
+    fun onDeviceAwake_activeMediaExists_mediaIsVisible() =
+        kosmos.runTest {
+            val userMedia = MediaData(active = true)
+
+            mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+            keyguardRepository.setIsDozing(false)
+
+            assertThat(underTest.isMediaVisible).isTrue()
+        }
+
+    @Test
+    fun onDeviceAwake_noActiveMedia_mediaIsHidden() =
+        kosmos.runTest {
+            keyguardRepository.setIsDozing(false)
+
+            assertThat(underTest.isMediaVisible).isFalse()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardWindowBlurTestUtilKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardWindowBlurTestUtilKosmos.kt
index ef07786..802885c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardWindowBlurTestUtilKosmos.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardWindowBlurTestUtilKosmos.kt
@@ -76,6 +76,21 @@
         }
     }
 
+    suspend fun assertNoBlurRadiusTransition(
+        transitionProgress: List<Float>,
+        actualValuesProvider: () -> List<Float>,
+        transitionFactory: (value: Float, state: TransitionState) -> TransitionStep,
+    ) {
+        val transitionSteps =
+            listOf(
+                transitionFactory(transitionProgress.first(), STARTED),
+                *transitionProgress.drop(1).map { transitionFactory(it, RUNNING) }.toTypedArray(),
+            )
+        fakeKeyguardTransitionRepository.sendTransitionSteps(transitionSteps, testScope)
+
+        assertThat(actualValuesProvider.invoke()).isEmpty()
+    }
+
     fun shadeExpanded(expanded: Boolean) {
         if (expanded) {
             shadeTestUtil.setQsExpansion(1f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index e4eb55b..5436d7e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -37,7 +36,7 @@
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
 import com.android.systemui.testKosmos
 import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
 import com.android.systemui.util.mockito.whenever
@@ -134,11 +133,11 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun areNotificationsVisible_dualShadeWideOnLockscreen_true() =
         with(kosmos) {
             testScope.runTest {
                 val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
+                kosmos.enableDualShade()
                 shadeRepository.setShadeLayoutWide(true)
                 fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
index a60a486..b5cee80 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
@@ -22,21 +22,22 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
@@ -53,7 +54,7 @@
 
     @Test
     fun lockscreenFadeOut() =
-        testScope.runTest {
+        kosmos.runTest {
             val values by collectValues(underTest.keyguardAlpha)
             assertThat(values).isEmpty()
 
@@ -82,10 +83,10 @@
 
     @Test
     fun lockscreenTranslationX() =
-        testScope.runTest {
+        kosmos.runTest {
             configurationRepository.setDimensionPixelSize(
                 R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x,
-                -100
+                -100,
             )
             val configuration = mock<Configuration>()
             whenever(configuration.layoutDirection).thenReturn(LayoutDirection.LTR)
@@ -108,16 +109,40 @@
             values.forEach { assertThat(it.value).isIn(Range.closed(-100f, 0f)) }
         }
 
+    @Test
+    @DisableSceneContainer
+    fun blurBecomesMaxValueImmediately() =
+        kosmos.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = blurConfig.minBlurRadiusPx,
+                endValue = blurConfig.maxBlurRadiusPx,
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.LOCKSCREEN,
+                        to = KeyguardState.GLANCEABLE_HUB,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "LockscreenToGlanceableHubTransitionViewModelTest",
+                    )
+                },
+                checkInterpolatedValues = false,
+            )
+        }
+
     private fun step(
         value: Float,
-        state: TransitionState = TransitionState.RUNNING
+        state: TransitionState = TransitionState.RUNNING,
     ): TransitionStep {
         return TransitionStep(
             from = KeyguardState.LOCKSCREEN,
             to = KeyguardState.GLANCEABLE_HUB,
             value = value,
             transitionState = state,
-            ownerName = this::class.java.simpleName
+            ownerName = this::class.java.simpleName,
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index fdee8d0..aaca603 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags.FLAG_BOUNCER_UI_REVAMP
+import com.android.systemui.Flags.FLAG_NOTIFICATION_SHADE_BLUR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
@@ -155,6 +156,7 @@
         }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_SHADE_BLUR)
     @BrokenWithSceneContainer(388068805)
     fun blurRadiusIsMaxWhenShadeIsExpanded() =
         testScope.runTest {
@@ -198,8 +200,8 @@
 
             kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
                 transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
-                startValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
-                endValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
+                startValue = kosmos.blurConfig.maxBlurRadiusPx,
+                endValue = kosmos.blurConfig.maxBlurRadiusPx,
                 transitionFactory = ::step,
                 actualValuesProvider = { values },
                 checkInterpolatedValues = false,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModelTest.kt
new file mode 100644
index 0000000..c657bdd
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModelTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class OccludedToGlanceableHubTransitionViewModelTest : SysuiTestCase() {
+    val kosmos = testKosmos()
+    val testScope = kosmos.testScope
+
+    val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
+    val underTest by lazy { kosmos.occludedToGlanceableHubTransitionViewModel }
+
+    @Test
+    @DisableSceneContainer
+    fun blurBecomesMaxValueImmediately() =
+        kosmos.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = blurConfig.minBlurRadiusPx,
+                endValue = blurConfig.maxBlurRadiusPx,
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.OCCLUDED,
+                        to = KeyguardState.GLANCEABLE_HUB,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "OccludedToGlanceableHubTransitionViewModelTest",
+                    )
+                },
+                checkInterpolatedValues = false,
+            )
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt
index 6db8767..0951df2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt
@@ -16,50 +16,96 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_NOTIFICATION_SHADE_BLUR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.BrokenWithSceneContainer
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.transitions.blurConfig
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @ExperimentalCoroutinesApi
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class OccludedToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class OccludedToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameterization) :
+    SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val underTest by lazy { kosmos.occludedToPrimaryBouncerTransitionViewModel }
+    private lateinit var underTest: OccludedToPrimaryBouncerTransitionViewModel
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    @Before
+    fun setup() {
+        underTest = kosmos.occludedToPrimaryBouncerTransitionViewModel
+    }
 
     @Test
-    @DisableSceneContainer
-    fun blurBecomesMaxValueImmediately() =
+    @BrokenWithSceneContainer(388068805)
+    fun notificationsAreBlurredImmediatelyWhenBouncerIsOpenedAndShadeIsExpanded() =
         testScope.runTest {
-            val values by collectValues(underTest.windowBlurRadius)
+            val values by collectValues(underTest.notificationBlurRadius)
+            kosmos.keyguardWindowBlurTestUtil.shadeExpanded(true)
 
             kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
                 transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
                 startValue = kosmos.blurConfig.maxBlurRadiusPx,
                 endValue = kosmos.blurConfig.maxBlurRadiusPx,
                 actualValuesProvider = { values },
-                transitionFactory = { step, transitionState ->
-                    TransitionStep(
-                        from = KeyguardState.OCCLUDED,
-                        to = KeyguardState.PRIMARY_BOUNCER,
-                        value = step,
-                        transitionState = transitionState,
-                        ownerName = "OccludedToPrimaryBouncerTransitionViewModelTest",
-                    )
-                },
+                transitionFactory = ::step,
                 checkInterpolatedValues = false,
             )
         }
+
+    @Test
+    @EnableFlags(FLAG_NOTIFICATION_SHADE_BLUR)
+    @BrokenWithSceneContainer(388068805)
+    fun blurBecomesMaxValueImmediatelyWhenShadeIsAlreadyExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+            kosmos.keyguardWindowBlurTestUtil.shadeExpanded(true)
+
+            kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = kosmos.blurConfig.maxBlurRadiusPx,
+                endValue = kosmos.blurConfig.maxBlurRadiusPx,
+                actualValuesProvider = { values },
+                transitionFactory = ::step,
+                checkInterpolatedValues = false,
+            )
+        }
+
+    fun step(value: Float, state: TransitionState = TransitionState.RUNNING): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.OCCLUDED,
+            to = KeyguardState.PRIMARY_BOUNCER,
+            value = value,
+            transitionState = state,
+            ownerName = "OccludedToPrimaryBouncerTransitionViewModelTest",
+        )
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt
index 9cfcce4..e4843bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelTest.kt
@@ -16,18 +16,20 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectValues
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.transitions.blurConfig
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -36,19 +38,19 @@
 @RunWith(AndroidJUnit4::class)
 class PrimaryBouncerToGlanceableHubTransitionViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
     private val underTest by lazy { kosmos.primaryBouncerToGlanceableHubTransitionViewModel }
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
     fun blurBecomesMinValueImmediately() =
-        testScope.runTest {
+        kosmos.runTest {
             val values by collectValues(underTest.windowBlurRadius)
 
-            kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+            keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
                 transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
-                startValue = kosmos.blurConfig.minBlurRadiusPx,
-                endValue = kosmos.blurConfig.minBlurRadiusPx,
+                startValue = blurConfig.maxBlurRadiusPx,
+                endValue = blurConfig.minBlurRadiusPx,
                 actualValuesProvider = { values },
                 transitionFactory = { step, transitionState ->
                     TransitionStep(
@@ -62,4 +64,26 @@
                 checkInterpolatedValues = false,
             )
         }
+
+    @Test
+    @DisableSceneContainer
+    @EnableFlags(FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
+    fun noBlurTransitionWithBlurredGlanceableHub() =
+        kosmos.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+
+            keyguardWindowBlurTestUtil.assertNoBlurRadiusTransition(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.PRIMARY_BOUNCER,
+                        to = KeyguardState.GLANCEABLE_HUB,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "PrimaryBouncerToGlanceableHubTransitionViewModelTest",
+                    )
+                },
+            )
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
index 0db0c5f..8fefb8d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
@@ -16,12 +16,16 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_NOTIFICATION_SHADE_BLUR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.BrokenWithSceneContainer
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -35,13 +39,17 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @ExperimentalCoroutinesApi
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class PrimaryBouncerToLockscreenTransitionViewModelTest(flags: FlagsParameterization) :
+    SysuiTestCase() {
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
 
@@ -49,9 +57,27 @@
     val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
     val biometricSettingsRepository = kosmos.biometricSettingsRepository
 
-    val underTest = kosmos.primaryBouncerToLockscreenTransitionViewModel
+    private lateinit var underTest: PrimaryBouncerToLockscreenTransitionViewModel
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    @Before
+    fun setup() {
+        underTest = kosmos.primaryBouncerToLockscreenTransitionViewModel
+    }
 
     @Test
+    @BrokenWithSceneContainer(392346450)
     fun lockscreenAlphaStartsFromViewStateAccessorAlpha() =
         testScope.runTest {
             val viewState = ViewStateAccessor(alpha = { 0.5f })
@@ -70,6 +96,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(392346450)
     fun deviceEntryParentViewAlpha() =
         testScope.runTest {
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
@@ -89,6 +116,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(392346450)
     fun deviceEntryBackgroundViewAlpha_udfpsEnrolled_show() =
         testScope.runTest {
             fingerprintPropertyRepository.supportsUdfps()
@@ -113,6 +141,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(388068805)
     fun blurRadiusGoesFromMaxToMinWhenShadeIsNotExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.windowBlurRadius)
@@ -128,6 +157,8 @@
         }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_SHADE_BLUR)
+    @BrokenWithSceneContainer(388068805)
     fun blurRadiusRemainsAtMaxWhenShadeIsExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.windowBlurRadius)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelTest.kt
index b0b4af5..fd7fb9f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelTest.kt
@@ -16,11 +16,13 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.platform.test.annotations.EnableFlags
 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.coroutines.collectValues
-import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.transitions.blurConfig
@@ -40,10 +42,37 @@
     private val underTest by lazy { kosmos.primaryBouncerToOccludedTransitionViewModel }
 
     @Test
-    @DisableSceneContainer
-    fun blurBecomesMaxValueImmediately() =
+    @BrokenWithSceneContainer(388068805)
+    fun blurBecomesMinValueImmediatelyWhenShadeIsNotExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.windowBlurRadius)
+            kosmos.keyguardWindowBlurTestUtil.shadeExpanded(false)
+
+            kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+                startValue = kosmos.blurConfig.minBlurRadiusPx,
+                endValue = kosmos.blurConfig.minBlurRadiusPx,
+                actualValuesProvider = { values },
+                transitionFactory = { step, transitionState ->
+                    TransitionStep(
+                        from = KeyguardState.PRIMARY_BOUNCER,
+                        to = KeyguardState.OCCLUDED,
+                        value = step,
+                        transitionState = transitionState,
+                        ownerName = "PrimaryBouncerToOccludedTransitionViewModelTest",
+                    )
+                },
+                checkInterpolatedValues = false,
+            )
+        }
+
+    @Test
+    @BrokenWithSceneContainer(388068805)
+    @EnableFlags(Flags.FLAG_NOTIFICATION_SHADE_BLUR)
+    fun blurBecomesMaxValueImmediatelyWhenShadeIsExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.windowBlurRadius)
+            kosmos.keyguardWindowBlurTestUtil.shadeExpanded(false)
 
             kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
                 transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaDataRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaDataRepositoryTest.kt
index 2864f04..7e6f5fc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaDataRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaDataRepositoryTest.kt
@@ -22,8 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.MediaTestHelper
 import com.android.systemui.media.controls.shared.model.MediaData
@@ -79,29 +77,6 @@
         }
 
     @Test
-    fun setRecommendationInactive() =
-        testScope.runTest {
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, true)
-            val smartspaceData by collectLastValue(underTest.smartspaceMediaData)
-            val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
-            val recommendation =
-                SmartspaceMediaData(
-                    targetId = KEY_MEDIA_SMARTSPACE,
-                    isActive = true,
-                    recommendations = MediaTestHelper.getValidRecommendationList(icon),
-                )
-
-            underTest.setRecommendation(recommendation)
-
-            assertThat(smartspaceData).isEqualTo(recommendation)
-
-            underTest.setRecommendationInactive(KEY_MEDIA_SMARTSPACE)
-
-            assertThat(smartspaceData).isNotEqualTo(recommendation)
-            assertThat(smartspaceData!!.isActive).isFalse()
-        }
-
-    @Test
     fun dismissRecommendation() =
         testScope.runTest {
             val smartspaceData by collectLastValue(underTest.smartspaceMediaData)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index 414974c..0a44e7b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -24,8 +24,6 @@
 import com.android.internal.logging.InstanceId
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.MediaTestHelper
 import com.android.systemui.media.controls.data.repository.MediaFilterRepository
@@ -140,7 +138,6 @@
             val hasAnyMediaOrRecommendation by
                 collectLastValue(underTest.hasAnyMediaOrRecommendation)
             val currentMedia by collectLastValue(underTest.currentMedia)
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
 
             val userMedia = MediaData(active = false)
             val recsLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
@@ -163,7 +160,7 @@
             assertThat(currentMedia)
                 .containsExactly(
                     MediaCommonModel.MediaRecommendations(recsLoadingModel),
-                    MediaCommonModel.MediaControl(mediaLoadingModel, true)
+                    MediaCommonModel.MediaControl(mediaLoadingModel, true),
                 )
                 .inOrder()
 
@@ -176,7 +173,7 @@
                     Process.INVALID_UID,
                     surface = SURFACE,
                     2,
-                    true
+                    true,
                 )
         }
 
@@ -187,7 +184,6 @@
                 collectLastValue(underTest.hasActiveMediaOrRecommendation)
             val hasAnyMediaOrRecommendation by
                 collectLastValue(underTest.hasAnyMediaOrRecommendation)
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
 
             mediaFilterRepository.setRecommendation(mediaRecommendation)
 
@@ -207,7 +203,6 @@
                 collectLastValue(underTest.hasActiveMediaOrRecommendation)
             val hasAnyMediaOrRecommendation by
                 collectLastValue(underTest.hasAnyMediaOrRecommendation)
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
 
             mediaFilterRepository.setRecommendation(mediaRecommendation)
 
@@ -248,7 +243,7 @@
                     active = true,
                     instanceId = instanceId,
                     packageName = PACKAGE_NAME,
-                    notificationKey = KEY
+                    notificationKey = KEY,
                 )
             val smartspaceLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
             val mediaLoadingModel = MediaDataLoadingModel.Loaded(instanceId)
@@ -269,7 +264,7 @@
             assertThat(currentMedia)
                 .containsExactly(
                     MediaCommonModel.MediaControl(mediaLoadingModel, isMediaFromRec = true),
-                    MediaCommonModel.MediaRecommendations(smartspaceLoadingModel)
+                    MediaCommonModel.MediaRecommendations(smartspaceLoadingModel),
                 )
                 .inOrder()
         }
@@ -282,7 +277,7 @@
                 active = true,
                 instanceId = instanceId,
                 packageName = PACKAGE_NAME,
-                notificationKey = KEY
+                notificationKey = KEY,
             )
         val smartspaceLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
         val mediaLoadingModel = MediaDataLoadingModel.Loaded(instanceId)
@@ -297,7 +292,7 @@
                 data.smartspaceId,
                 data.appUid,
                 surface = SURFACE,
-                1
+                1,
             )
 
         reset(smartspaceLogger)
@@ -311,7 +306,7 @@
                 data.smartspaceId,
                 data.appUid,
                 surface = SURFACE,
-                2
+                2,
             )
 
         reset(smartspaceLogger)
@@ -327,7 +322,7 @@
                 surface = SURFACE,
                 2,
                 true,
-                rank = 1
+                rank = 1,
             )
 
         reset(smartspaceLogger)
@@ -343,7 +338,7 @@
                 data.smartspaceId,
                 data.appUid,
                 surface = SURFACE,
-                2
+                2,
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
index 8af7e1d..11397d9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
@@ -29,8 +29,6 @@
 import com.android.systemui.broadcast.broadcastSender
 import com.android.systemui.broadcast.mockBroadcastSender
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.MediaTestHelper
 import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
@@ -104,8 +102,8 @@
                         listOf(
                             MediaRecModel(icon = icon),
                             MediaRecModel(icon = icon),
-                            MediaRecModel(icon = icon)
-                        )
+                            MediaRecModel(icon = icon),
+                        ),
                 )
 
             mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
@@ -114,22 +112,6 @@
         }
 
     @Test
-    fun setRecommendationInactive_isActiveUpdate() =
-        testScope.runTest {
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, true)
-            val isActive by collectLastValue(underTest.isActive)
-
-            mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
-            assertThat(isActive).isTrue()
-
-            mediaDataFilter.onSmartspaceMediaDataLoaded(
-                KEY_MEDIA_SMARTSPACE,
-                smartspaceMediaData.copy(isActive = false)
-            )
-            assertThat(isActive).isFalse()
-        }
-
-    @Test
     fun addInvalidRecommendation() =
         testScope.runTest {
             val recommendations by collectLastValue(underTest.recommendations)
@@ -155,7 +137,7 @@
             intent,
             0,
             SMARTSPACE_CARD_DISMISS_EVENT,
-            1
+            1,
         )
 
         verify(smartspaceLogger)
@@ -183,7 +165,7 @@
             intent,
             0,
             SMARTSPACE_CARD_DISMISS_EVENT,
-            1
+            1,
         )
 
         verify(spyContext).startActivity(eq(intent))
@@ -216,7 +198,7 @@
                 cardinality = 1,
                 isRecommendationCard = true,
                 interactedSubcardRank = 2,
-                interactedSubcardCardinality = 3
+                interactedSubcardCardinality = 3,
             )
         verify(spyContext).startActivity(eq(intent))
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index 51c8525..a3b3f5c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -96,7 +96,6 @@
     @Before
     fun setup() {
         whenever(mediaControllerFactory.create(any())).thenReturn(mediaController)
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
         mainExecutor = FakeExecutor(clock)
         bgExecutor = FakeExecutor(clock)
         uiExecutor = FakeExecutor(clock)
@@ -612,89 +611,6 @@
         assertThat(mainExecutor.numPending()).isEqualTo(1)
     }
 
-    @Test
-    fun testSmartspaceDataLoaded_schedulesTimeout() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-        val duration = 60_000
-        val createTime = 1234L
-        val expireTime = createTime + duration
-        whenever(smartspaceData.headphoneConnectionTimeMillis).thenReturn(createTime)
-        whenever(smartspaceData.expiryTimeMs).thenReturn(expireTime)
-
-        mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
-        assertThat(mainExecutor.numPending()).isEqualTo(1)
-        assertThat(mainExecutor.advanceClockToNext()).isEqualTo(duration)
-    }
-
-    @Test
-    fun testSmartspaceMediaData_timesOut_invokesCallback() {
-        // Given a pending timeout
-        testSmartspaceDataLoaded_schedulesTimeout()
-
-        mainExecutor.runAllReady()
-        verify(timeoutCallback).invoke(eq(SMARTSPACE_KEY), eq(true))
-    }
-
-    @Test
-    fun testSmartspaceDataLoaded_alreadyExists_updatesTimeout() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-        val duration = 100
-        val createTime = 1234L
-        val expireTime = createTime + duration
-        whenever(smartspaceData.headphoneConnectionTimeMillis).thenReturn(createTime)
-        whenever(smartspaceData.expiryTimeMs).thenReturn(expireTime)
-
-        mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
-        assertThat(mainExecutor.numPending()).isEqualTo(1)
-
-        val expiryLonger = expireTime + duration
-        whenever(smartspaceData.expiryTimeMs).thenReturn(expiryLonger)
-        mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
-
-        assertThat(mainExecutor.numPending()).isEqualTo(1)
-        assertThat(mainExecutor.advanceClockToNext()).isEqualTo(duration * 2)
-    }
-
-    @Test
-    fun testSmartspaceDataRemoved_cancelTimeout() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-
-        mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
-        assertThat(mainExecutor.numPending()).isEqualTo(1)
-
-        mediaTimeoutListener.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
-        assertThat(mainExecutor.numPending()).isEqualTo(0)
-    }
-
-    @Test
-    fun testSmartspaceData_dozedPastTimeout_invokedOnWakeup() {
-        // Given a pending timeout
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-        verify(statusBarStateController).addCallback(capture(dozingCallbackCaptor))
-        val duration = 60_000
-        val createTime = 1234L
-        val expireTime = createTime + duration
-        whenever(smartspaceData.headphoneConnectionTimeMillis).thenReturn(createTime)
-        whenever(smartspaceData.expiryTimeMs).thenReturn(expireTime)
-
-        mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
-        assertThat(mainExecutor.numPending()).isEqualTo(1)
-
-        // And we doze past the scheduled timeout
-        val time = clock.currentTimeMillis()
-        clock.setElapsedRealtime(time + duration * 2)
-        assertThat(mainExecutor.numPending()).isEqualTo(1)
-
-        // Then when no longer dozing, the timeout runs immediately
-        dozingCallbackCaptor.value.onDozingChanged(false)
-        verify(timeoutCallback).invoke(eq(SMARTSPACE_KEY), eq(true))
-        verify(logger).logTimeout(eq(SMARTSPACE_KEY))
-
-        // and cancel any later scheduled timeout
-        assertThat(mainExecutor.numPending()).isEqualTo(0)
-    }
-
     private fun loadMediaDataWithPlaybackState(state: PlaybackState) {
         whenever(mediaController.playbackState).thenReturn(state)
         loadMediaData(data = mediaData)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
index 067b00c..fb5bbf4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
@@ -25,8 +25,6 @@
 import com.android.internal.logging.InstanceId
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.MediaTestHelper
 import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
@@ -126,7 +124,6 @@
     fun loadMediaControlsAndRecommendations_mediaItemsAreUpdated() =
         testScope.runTest {
             val sortedMedia by collectLastValue(underTest.mediaItems)
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             val instanceId1 = InstanceId.fakeInstanceId(123)
             val instanceId2 = InstanceId.fakeInstanceId(456)
 
@@ -147,7 +144,6 @@
         testScope.runTest {
             val sortedMedia by collectLastValue(underTest.mediaItems)
             kosmos.visualStabilityProvider.isReorderingAllowed = false
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             val instanceId = InstanceId.fakeInstanceId(123)
 
             loadMediaRecommendations()
@@ -207,7 +203,6 @@
     fun addMediaRecommendationThenRemove_mediaEventsAreLogged() =
         testScope.runTest {
             val sortedMedia by collectLastValue(underTest.mediaItems)
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
 
             loadMediaRecommendations()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 63ec78fd..57ac906 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -117,8 +117,8 @@
                 LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
         mMediaDevices.add(mMediaDevice1);
         mMediaDevices.add(mMediaDevice2);
-        mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice1));
-        mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice2));
+        mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice1, true));
+        mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice2, false));
 
         mMediaOutputAdapter = new MediaOutputAdapter(mMediaSwitchingController);
         mMediaOutputAdapter.updateItems();
@@ -156,11 +156,10 @@
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
 
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
-                R.string.media_output_dialog_pairing_new));
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
+                mContext.getText(R.string.media_output_dialog_pairing_new).toString());
     }
 
     @Test
@@ -179,7 +178,6 @@
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
@@ -201,7 +199,6 @@
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
@@ -216,7 +213,6 @@
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
@@ -232,7 +228,6 @@
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
@@ -250,7 +245,6 @@
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
     }
@@ -265,7 +259,6 @@
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
         assertThat(mViewHolder.mSeekBar.getContentDescription()).isNotNull();
-        assertThat(mViewHolder.mSeekBar.getAccessibilityDelegate()).isNotNull();
         assertThat(mViewHolder.mContainerLayout.isFocusable()).isFalse();
     }
 
@@ -282,7 +275,6 @@
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.GONE);
     }
@@ -302,7 +294,6 @@
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
     }
@@ -324,7 +315,6 @@
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
     }
@@ -335,7 +325,6 @@
         when(mMediaSwitchingController.isCurrentConnectedDeviceRemote()).thenReturn(false);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -351,7 +340,6 @@
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -393,7 +381,6 @@
         when(mMediaSwitchingController.getSelectableMediaDevice()).thenReturn(selectableDevices);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
 
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -405,7 +392,6 @@
     public void onBindViewHolder_bindNonActiveConnectedDevice_verifyView() {
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
 
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -423,7 +409,6 @@
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
                 TEST_DEVICE_NAME_2);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
@@ -432,15 +417,14 @@
                 LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
 
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(mContext.getText(
-                R.string.media_output_dialog_connect_failed));
-        assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+        assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(
+                mContext.getText(R.string.media_output_dialog_connect_failed).toString());
     }
 
     @Test
@@ -456,17 +440,15 @@
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(TEST_CUSTOM_SUBTEXT);
-        assertThat(mViewHolder.mTwoLineTitleText.getText().toString()).isEqualTo(
-                TEST_DEVICE_NAME_1);
         assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isFalse();
     }
 
@@ -482,15 +464,14 @@
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
 
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(deviceStatus);
-        assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+        assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(deviceStatus);
         assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isTrue();
     }
 
@@ -506,16 +487,14 @@
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
 
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(deviceStatus);
-        assertThat(mViewHolder.mTwoLineTitleText.getText().toString()).isEqualTo(
-                TEST_DEVICE_NAME_2);
         assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isFalse();
     }
 
@@ -530,16 +509,14 @@
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(TEST_CUSTOM_SUBTEXT);
-        assertThat(mViewHolder.mTwoLineTitleText.getText().toString()).isEqualTo(
-                TEST_DEVICE_NAME_1);
         assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isFalse();
     }
 
@@ -556,7 +533,6 @@
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
@@ -572,7 +548,6 @@
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
@@ -582,11 +557,10 @@
                 LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
     }
 
     @Test
@@ -805,4 +779,120 @@
                 mViewHolder.getDrawableId(false /* isInputDevice */, false /* isMutedVolumeIcon */))
                 .isEqualTo(R.drawable.media_output_icon_volume);
     }
+
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void multipleSelectedDevices_verifySessionView() {
+        initializeSession();
+
+        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+                .onCreateViewHolder(
+                        new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_SESSION_NAME);
+        assertThat(mViewHolder.mSeekBar.getVolume()).isEqualTo(TEST_CURRENT_VOLUME);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void multipleSelectedDevices_verifyCollapsedView() {
+        initializeSession();
+
+        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+                .onCreateViewHolder(
+                        new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+        assertThat(mViewHolder.mItemLayout.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void multipleSelectedDevices_expandIconClicked_verifyInitialView() {
+        initializeSession();
+        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+                .onCreateViewHolder(
+                        new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        mViewHolder.mEndTouchArea.performClick();
+        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+                .onCreateViewHolder(
+                        new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void multipleSelectedDevices_expandIconClicked_verifyCollapsedView() {
+        initializeSession();
+        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+                .onCreateViewHolder(
+                        new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        mViewHolder.mEndTouchArea.performClick();
+        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+                .onCreateViewHolder(
+                        new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void deviceCanNotBeDeselected_verifyView() {
+        List<MediaDevice> selectedDevices = new ArrayList<>();
+        selectedDevices.add(mMediaDevice1);
+        when(mMediaSwitchingController.getSelectableMediaDevice()).thenReturn(selectedDevices);
+        when(mMediaSwitchingController.getSelectedMediaDevice()).thenReturn(selectedDevices);
+        when(mMediaSwitchingController.getDeselectableMediaDevice()).thenReturn(new ArrayList<>());
+
+        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+                .onCreateViewHolder(
+                        new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
+    }
+
+    private void initializeSession() {
+        when(mMediaSwitchingController.getSessionVolumeMax()).thenReturn(TEST_MAX_VOLUME);
+        when(mMediaSwitchingController.getSessionVolume()).thenReturn(TEST_CURRENT_VOLUME);
+        when(mMediaSwitchingController.getSessionName()).thenReturn(TEST_SESSION_NAME);
+
+        List<MediaDevice> selectedDevices = new ArrayList<>();
+        selectedDevices.add(mMediaDevice1);
+        selectedDevices.add(mMediaDevice2);
+        when(mMediaSwitchingController.getSelectableMediaDevice()).thenReturn(selectedDevices);
+        when(mMediaSwitchingController.getSelectedMediaDevice()).thenReturn(selectedDevices);
+        when(mMediaSwitchingController.getDeselectableMediaDevice()).thenReturn(selectedDevices);
+
+        mMediaOutputAdapter.updateItems();
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/OWNERS
new file mode 100644
index 0000000..739d2ac
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/OWNERS
@@ -0,0 +1 @@
+file:/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
index ff00bfb5..63942072 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.mediaprojection.data.repository
 
 import android.hardware.display.displayManager
+import android.media.projection.MediaProjectionEvent
 import android.media.projection.MediaProjectionInfo
 import android.media.projection.StopReason
 import android.os.Binder
@@ -32,8 +33,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -42,7 +46,7 @@
 import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeTasksRepository
 import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
 import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
-import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -55,7 +59,7 @@
 @SmallTest
 class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
 
-    private val kosmos = taskSwitcherKosmos()
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val testScope = kosmos.testScope
 
     private val fakeMediaProjectionManager = kosmos.fakeMediaProjectionManager
@@ -345,4 +349,40 @@
             verify(fakeMediaProjectionManager.mediaProjectionManager)
                 .stopActiveProjection(StopReason.STOP_QS_TILE)
         }
+
+    @Test
+    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun projectionStartedDuringCallAndActivePostCallEvent_flagEnabled_emitsUnit() =
+        kosmos.runTest {
+            val projectionStartedDuringCallAndActivePostCallEvent by
+                collectLastValue(repo.projectionStartedDuringCallAndActivePostCallEvent)
+
+            fakeMediaProjectionManager.dispatchEvent(
+                PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL_EVENT
+            )
+
+            assertThat(projectionStartedDuringCallAndActivePostCallEvent).isEqualTo(Unit)
+        }
+
+    @Test
+    @DisableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun projectionStartedDuringCallAndActivePostCallEvent_flagDisabled_doesNotEmit() =
+        testScope.runTest {
+            val projectionStartedDuringCallAndActivePostCallEvent by
+                collectLastValue(repo.projectionStartedDuringCallAndActivePostCallEvent)
+
+            fakeMediaProjectionManager.dispatchEvent(
+                PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL_EVENT
+            )
+
+            assertThat(projectionStartedDuringCallAndActivePostCallEvent).isNull()
+        }
+
+    companion object {
+        private val PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL_EVENT =
+            MediaProjectionEvent(
+                MediaProjectionEvent.PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL,
+                /* timestampMillis= */ 100L,
+            )
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index a770ee1..c1872f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -57,7 +57,7 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
@@ -104,7 +104,7 @@
     @Mock
     SystemActions mSystemActions;
     @Mock
-    OverviewProxyService mOverviewProxyService;
+    LauncherProxyService mLauncherProxyService;
     @Mock
     Lazy<AssistManager> mAssistManagerLazy;
     @Mock
@@ -161,7 +161,7 @@
         mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
                 mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
                 mAccessibilityGestureTargetObserver,
-                mSystemActions, mOverviewProxyService, mAssistManagerLazy,
+                mSystemActions, mLauncherProxyService, mAssistManagerLazy,
                 () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
                 mNavigationModeController, mEdgeBackGestureHandlerFactory, mWm, mUserTracker,
                 mDisplayTracker, mNotificationShadeWindowController, mConfigurationController,
@@ -171,7 +171,7 @@
     @Test
     public void registerListenersInCtor() {
         verify(mNavigationModeController, times(1)).addListener(mNavBarHelper);
-        verify(mOverviewProxyService, times(1)).addCallback(mNavBarHelper);
+        verify(mLauncherProxyService, times(1)).addCallback(mNavBarHelper);
         verify(mCommandQueue, times(1)).addCallback(any());
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
index 9bae7bd..cf0a250 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
@@ -9,7 +9,7 @@
 import com.android.systemui.model.SysUiState
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.recents.LauncherProxyService
 import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.shared.system.QuickStepContract
 import com.android.systemui.shared.system.TaskStackChangeListeners
@@ -49,7 +49,7 @@
     @Mock lateinit var mLightBarControllerFactory: LightBarTransitionsController.Factory
     @Mock lateinit var mLightBarTransitionController: LightBarTransitionsController
     @Mock lateinit var mCommandQueue: CommandQueue
-    @Mock lateinit var mOverviewProxyService: OverviewProxyService
+    @Mock lateinit var mLauncherProxyService: LauncherProxyService
     @Mock lateinit var mNavBarHelper: NavBarHelper
     @Mock lateinit var mNavigationModeController: NavigationModeController
     @Mock lateinit var mSysUiState: SysUiState
@@ -87,7 +87,7 @@
             )
         mTaskbarDelegate.setDependencies(
             mCommandQueue,
-            mOverviewProxyService,
+            mLauncherProxyService,
             mNavBarHelper,
             mNavigationModeController,
             mSysUiState,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java
index 00e79f5..fd4bb4b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java
@@ -40,7 +40,7 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -80,7 +80,7 @@
                 .thenReturn(mEdgeBackGestureHandler);
 
         mDependency.injectMockDependency(AssistManager.class);
-        mDependency.injectMockDependency(OverviewProxyService.class);
+        mDependency.injectMockDependency(LauncherProxyService.class);
         mDependency.injectMockDependency(KeyguardStateController.class);
         mDependency.injectMockDependency(NavigationBarController.class);
         mDependency.injectTestDependency(EdgeBackGestureHandler.Factory.class,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java
index e58c8f2..85c093c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java
@@ -35,7 +35,7 @@
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.navigationbar.views.buttons.ButtonDispatcher;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -55,7 +55,7 @@
     @Before
     public void setUp() {
         mDependency.injectMockDependency(AssistManager.class);
-        mDependency.injectMockDependency(OverviewProxyService.class);
+        mDependency.injectMockDependency(LauncherProxyService.class);
         mDependency.injectMockDependency(NavigationModeController.class);
         mDependency.injectMockDependency(NavigationBarController.class);
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index 7f9313c..09e49eb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -16,9 +16,9 @@
 
 package com.android.systemui.navigationbar.views;
 
-import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.app.StatusBarManager.NAVBAR_BACK_DISMISS_IME;
+import static android.app.StatusBarManager.NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
+import static android.app.StatusBarManager.NAVBAR_IME_VISIBLE;
 import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING;
 import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
 import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
@@ -31,8 +31,9 @@
 import static com.android.systemui.navigationbar.views.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
 import static com.android.systemui.navigationbar.views.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_LONGPRESS;
 import static com.android.systemui.navigationbar.views.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISMISS_IME;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -105,7 +106,7 @@
 import com.android.systemui.navigationbar.views.buttons.NavBarButtonClickLogger;
 import com.android.systemui.navigationbar.views.buttons.NavbarOrientationTrackingLogger;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.FakeDisplayTracker;
@@ -184,7 +185,7 @@
     @Mock
     private SystemActions mSystemActions;
     @Mock
-    private OverviewProxyService mOverviewProxyService;
+    private LauncherProxyService mLauncherProxyService;
     @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
@@ -284,14 +285,14 @@
         mDependency.injectMockDependency(KeyguardStateController.class);
         mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
         mDependency.injectMockDependency(NavigationBarController.class);
-        mDependency.injectTestDependency(OverviewProxyService.class, mOverviewProxyService);
+        mDependency.injectTestDependency(LauncherProxyService.class, mLauncherProxyService);
         mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
         TestableLooper.get(this).runWithLooper(() -> {
             mNavBarHelper = spy(new NavBarHelper(mContext, mock(AccessibilityManager.class),
                     mock(AccessibilityButtonModeObserver.class),
                     mock(AccessibilityButtonTargetsObserver.class),
                     mock(AccessibilityGestureTargetsObserver.class),
-                    mSystemActions, mOverviewProxyService,
+                    mSystemActions, mLauncherProxyService,
                     () -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
                     mKeyguardStateController, mock(NavigationModeController.class),
                     mEdgeBackGestureHandlerFactory, mock(IWindowManager.class),
@@ -500,8 +501,9 @@
 
         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
-        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
-        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(true));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_VISIBLE), eq(true));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE), eq(true));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_BACK_DISMISS_IME), eq(true));
     }
 
     /**
@@ -514,8 +516,9 @@
 
         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, false /* showImeSwitcher */);
-        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
-        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(false));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_VISIBLE), eq(true));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE), eq(false));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_BACK_DISMISS_IME), eq(true));
     }
 
     /**
@@ -531,8 +534,9 @@
 
         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, 0 /* vis */,
                 BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
-        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(false));
-        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(false));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_VISIBLE), eq(false));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE), eq(false));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_BACK_DISMISS_IME), eq(false));
     }
 
     /**
@@ -545,8 +549,9 @@
 
         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
                 BACK_DISPOSITION_ADJUST_NOTHING, true /* showImeSwitcher */);
-        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
-        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(true));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_VISIBLE), eq(true));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE), eq(true));
+        verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_BACK_DISMISS_IME), eq(false));
     }
 
     @Test
@@ -564,29 +569,27 @@
         externalNavBar.init();
 
         defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
-                BACK_DISPOSITION_DEFAULT, true);
+                BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
 
         // Verify IME window state will be updated in default NavBar & external NavBar state reset.
-        assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
-                        | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
-                defaultNavBar.getNavigationIconHints());
-        assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
-        assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
-        assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
-                != 0);
+        assertEquals(NAVBAR_BACK_DISMISS_IME | NAVBAR_IME_VISIBLE
+                        | NAVBAR_IME_SWITCHER_BUTTON_VISIBLE,
+                defaultNavBar.getNavbarFlags());
+        assertFalse((externalNavBar.getNavbarFlags() & NAVBAR_BACK_DISMISS_IME) != 0);
+        assertFalse((externalNavBar.getNavbarFlags() & NAVBAR_IME_VISIBLE) != 0);
+        assertFalse((externalNavBar.getNavbarFlags() & NAVBAR_IME_SWITCHER_BUTTON_VISIBLE) != 0);
 
         externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, IME_VISIBLE,
-                BACK_DISPOSITION_DEFAULT, true);
+                BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
         defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, 0 /* vis */,
-                BACK_DISPOSITION_DEFAULT, false);
+                BACK_DISPOSITION_DEFAULT, false /* showImeSwitcher */);
         // Verify IME window state will be updated in external NavBar & default NavBar state reset.
-        assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
-                        | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
-                externalNavBar.getNavigationIconHints());
-        assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
-        assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
-        assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
-                != 0);
+        assertEquals(NAVBAR_BACK_DISMISS_IME | NAVBAR_IME_VISIBLE
+                        | NAVBAR_IME_SWITCHER_BUTTON_VISIBLE,
+                externalNavBar.getNavbarFlags());
+        assertFalse((defaultNavBar.getNavbarFlags() & NAVBAR_BACK_DISMISS_IME) != 0);
+        assertFalse((defaultNavBar.getNavbarFlags() & NAVBAR_IME_VISIBLE) != 0);
+        assertFalse((defaultNavBar.getNavbarFlags() & NAVBAR_IME_SWITCHER_BUTTON_VISIBLE) != 0);
     }
 
     @Test
@@ -601,32 +604,29 @@
 
         // Verify navbar altered back icon when an app is showing IME
         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
-                BACK_DISPOSITION_DEFAULT, true);
-        assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
-        assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
-        assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
-                != 0);
+                BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
+        assertTrue((mNavigationBar.getNavbarFlags() & NAVBAR_BACK_DISMISS_IME) != 0);
+        assertTrue((mNavigationBar.getNavbarFlags() & NAVBAR_IME_VISIBLE) != 0);
+        assertTrue((mNavigationBar.getNavbarFlags() & NAVBAR_IME_SWITCHER_BUTTON_VISIBLE) != 0);
 
         // Verify navbar didn't alter and showing back icon when the keyguard is showing without
         // requesting IME insets visible.
         doReturn(true).when(mKeyguardStateController).isShowing();
         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
-                BACK_DISPOSITION_DEFAULT, true);
-        assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
-        assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
-        assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
-                != 0);
+                BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
+        assertFalse((mNavigationBar.getNavbarFlags() & NAVBAR_BACK_DISMISS_IME) != 0);
+        assertFalse((mNavigationBar.getNavbarFlags() & NAVBAR_IME_VISIBLE) != 0);
+        assertFalse((mNavigationBar.getNavbarFlags() & NAVBAR_IME_SWITCHER_BUTTON_VISIBLE) != 0);
 
         // Verify navbar altered and showing back icon when the keyguard is showing and
         // requesting IME insets visible.
         windowInsets = new WindowInsets.Builder().setVisible(ime(), true).build();
         doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
-                BACK_DISPOSITION_DEFAULT, true);
-        assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
-        assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
-        assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
-                != 0);
+                BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
+        assertTrue((mNavigationBar.getNavbarFlags() & NAVBAR_BACK_DISMISS_IME) != 0);
+        assertTrue((mNavigationBar.getNavbarFlags() & NAVBAR_IME_VISIBLE) != 0);
+        assertTrue((mNavigationBar.getNavbarFlags() & NAVBAR_IME_SWITCHER_BUTTON_VISIBLE) != 0);
     }
 
     @Test
@@ -690,7 +690,7 @@
                 mock(AccessibilityManager.class),
                 deviceProvisionedController,
                 new MetricsLogger(),
-                mOverviewProxyService,
+                mLauncherProxyService,
                 mNavigationModeController,
                 mStatusBarStateController,
                 mStatusBarKeyguardViewManager,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java
index 3621ab9..cff9bec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java
@@ -36,7 +36,7 @@
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.shared.statusbar.phone.BarTransitions;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -72,7 +72,7 @@
         when(mEdgeBackGestureHandlerFactory.create(any(Context.class)))
                 .thenReturn(mEdgeBackGestureHandler);
         mDependency.injectMockDependency(AssistManager.class);
-        mDependency.injectMockDependency(OverviewProxyService.class);
+        mDependency.injectMockDependency(LauncherProxyService.class);
         mDependency.injectMockDependency(StatusBarStateController.class);
         mDependency.injectMockDependency(KeyguardStateController.class);
         mDependency.injectMockDependency(NavigationBarController.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java
index 403a883..58ec0c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java
@@ -51,7 +51,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -76,7 +76,7 @@
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
         mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
-        mDependency.injectMockDependency(OverviewProxyService.class);
+        mDependency.injectMockDependency(LauncherProxyService.class);
         mDependency.injectMockDependency(AssistManager.class);
         mUiEventLogger = mDependency.injectMockDependency(UiEventLogger.class);
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
index 855931c..52b9e47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
@@ -21,7 +21,9 @@
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.Back
 import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.UserActionResult.HideOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
@@ -53,7 +55,7 @@
             val actions by collectLastValue(underTest.actions)
             underTest.activateIn(this)
 
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
+            assertThat((actions?.get(Swipe.Up) as? HideOverlay)?.overlay)
                 .isEqualTo(Overlays.NotificationsShade)
             assertThat(actions?.get(Swipe.Down)).isNull()
         }
@@ -64,7 +66,7 @@
             val actions by collectLastValue(underTest.actions)
             underTest.activateIn(this)
 
-            assertThat((actions?.get(Back) as? UserActionResult.HideOverlay)?.overlay)
+            assertThat((actions?.get(Back) as? HideOverlay)?.overlay)
                 .isEqualTo(Overlays.NotificationsShade)
         }
 
@@ -74,11 +76,11 @@
             val actions by collectLastValue(underTest.actions)
             underTest.activateIn(this)
 
-            assertThat(
-                    (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopRight))
-                            as? UserActionResult.ReplaceByOverlay)
-                        ?.overlay
-                )
-                .isEqualTo(Overlays.QuickSettingsShade)
+            val action =
+                (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopRight)) as? ShowOverlay)
+            assertThat(action?.overlay).isEqualTo(Overlays.QuickSettingsShade)
+            val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some
+            assertThat(overlaysToHide).isNotNull()
+            assertThat(overlaysToHide?.overlays).containsExactly(Overlays.NotificationsShade)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
index b30313e..6759608 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.notifications.ui.viewmodel
 
-import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -36,8 +35,8 @@
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.enableDualShade
 import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayContentViewModel
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
@@ -56,7 +55,6 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @EnableSceneContainer
-@EnableFlags(DualShade.FLAG_NAME)
 class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
@@ -68,6 +66,7 @@
     @Before
     fun setUp() {
         kosmos.sceneContainerStartable.start()
+        kosmos.enableDualShade()
         underTest.activateIn(testScope)
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt
deleted file mode 100644
index 46b02e92..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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.notifications.ui.viewmodel
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.ui.viewmodel.notificationsShadeUserActionsViewModel
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-@EnableSceneContainer
-class NotificationsShadeUserActionsViewModelTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val sceneInteractor by lazy { kosmos.sceneInteractor }
-    private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
-
-    private val underTest by lazy { kosmos.notificationsShadeUserActionsViewModel }
-
-    @Test
-    fun upTransitionSceneKey_deviceLocked_lockscreen() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            lockDevice()
-            underTest.activateIn(this)
-
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Down)).isNull()
-            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
-                .isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            lockDevice()
-            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
-            underTest.activateIn(this)
-
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionSceneKey_deviceUnlocked_gone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            lockDevice()
-            unlockDevice()
-            underTest.activateIn(this)
-
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Down)).isNull()
-            assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-            underTest.activateIn(this)
-
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
-                .isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
-        testScope.runTest {
-            val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-            val actions by collectLastValue(underTest.actions)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
-            sceneInteractor // force the lazy; this will kick off StateFlows
-            runCurrent()
-            sceneInteractor.changeScene(Scenes.Gone, "reason")
-            underTest.activateIn(this)
-
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
-        }
-
-    private fun TestScope.lockDevice() {
-        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-        assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
-        sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-        runCurrent()
-    }
-
-    private fun TestScope.unlockDevice() {
-        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-        kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-            SuccessFingerprintAuthenticationStatus(0, true)
-        )
-        assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
-        sceneInteractor.changeScene(Scenes.Gone, "reason")
-        runCurrent()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index e9633f4..ff005c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.qs;
 
-import static com.android.systemui.Flags.FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS;
 import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -41,8 +40,6 @@
 
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.ContextThemeWrapper;
@@ -87,6 +84,7 @@
 
 import kotlinx.coroutines.flow.MutableStateFlow;
 import kotlinx.coroutines.flow.StateFlow;
+
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
 import platform.test.runner.parameterized.Parameters;
 
@@ -505,7 +503,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
     public void setTiles_longPressEffectEnabled_nonNullLongPressEffectsAreProvided() {
         mLongPressEffectProvider.mEffectsProvided = 0;
         when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
@@ -516,16 +513,6 @@
     }
 
     @Test
-    @DisableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
-    public void setTiles_longPressEffectDisabled_noLongPressEffectsAreProvided() {
-        mLongPressEffectProvider.mEffectsProvided = 0;
-        when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
-        mController.setTiles();
-
-        assertThat(mLongPressEffectProvider.mEffectsProvided).isEqualTo(0);
-    }
-
-    @Test
     public void setTiles_differentTiles_extraTileRemoved() {
         when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
         mController.setTiles();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index f90e1e9..f89d70b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
 import com.android.systemui.res.R
 import com.android.systemui.security.data.model.SecurityModel
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.policy.FakeSecurityController
 import com.android.systemui.statusbar.policy.FakeUserInfoController
 import com.android.systemui.statusbar.policy.FakeUserInfoController.FakeInfo
@@ -45,6 +46,7 @@
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.settings.FakeGlobalSettings
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.StandardTestDispatcher
@@ -57,6 +59,7 @@
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.`when` as whenever
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
@@ -78,51 +81,45 @@
 
     @Test
     fun settingsButton() = runTest {
-        val underTest = utils.footerActionsViewModel(showPowerButton = false)
+        val underTest =
+            utils.footerActionsViewModel(showPowerButton = false, shadeMode = ShadeMode.Single)
         val settings = underTest.settings
 
         assertThat(settings.icon)
             .isEqualTo(
                 Icon.Resource(
                     R.drawable.ic_settings,
-                    ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+                    ContentDescription.Resource(R.string.accessibility_quick_settings_settings),
                 )
             )
         assertThat(settings.backgroundColor).isEqualTo(R.attr.shadeInactive)
         assertThat(settings.iconTint)
-            .isEqualTo(
-                Utils.getColorAttrDefaultColor(
-                    themedContext,
-                    R.attr.onShadeInactiveVariant,
-                )
-            )
+            .isEqualTo(Utils.getColorAttrDefaultColor(themedContext, R.attr.onShadeInactiveVariant))
     }
 
     @Test
     fun powerButton() = runTest {
         // Without power button.
-        val underTestWithoutPower = utils.footerActionsViewModel(showPowerButton = false)
-        assertThat(underTestWithoutPower.power).isNull()
+        val underTestWithoutPower =
+            utils.footerActionsViewModel(showPowerButton = false, shadeMode = ShadeMode.Single)
+        val withoutPower by collectLastValue(underTestWithoutPower.power)
+        assertThat(withoutPower).isNull()
 
         // With power button.
-        val underTestWithPower = utils.footerActionsViewModel(showPowerButton = true)
-        val power = underTestWithPower.power
+        val underTestWithPower =
+            utils.footerActionsViewModel(showPowerButton = true, shadeMode = ShadeMode.Single)
+        val power by collectLastValue(underTestWithPower.power)
         assertThat(power).isNotNull()
-        assertThat(power!!.icon)
+        assertThat(checkNotNull(power).icon)
             .isEqualTo(
                 Icon.Resource(
                     android.R.drawable.ic_lock_power_off,
-                    ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+                    ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu),
                 )
             )
-        assertThat(power.backgroundColor).isEqualTo(R.attr.shadeActive)
-        assertThat(power.iconTint)
-            .isEqualTo(
-                Utils.getColorAttrDefaultColor(
-                    themedContext,
-                    R.attr.onShadeActive,
-                ),
-            )
+        assertThat(checkNotNull(power).backgroundColor).isEqualTo(R.attr.shadeActive)
+        assertThat(checkNotNull(power).iconTint)
+            .isEqualTo(Utils.getColorAttrDefaultColor(themedContext, R.attr.onShadeActive))
     }
 
     @Test
@@ -130,7 +127,6 @@
         val picture: Drawable = mock()
         val userInfoController = FakeUserInfoController(FakeInfo(picture = picture))
         val settings = FakeGlobalSettings(testDispatcher)
-        val userId = 42
         val userSwitcherControllerWrapper =
             MockUserSwitcherControllerWrapper(currentUserName = "foo")
 
@@ -144,6 +140,7 @@
         val underTest =
             utils.footerActionsViewModel(
                 showPowerButton = false,
+                shadeMode = ShadeMode.Single,
                 footerActionsInteractor =
                     utils.footerActionsInteractor(
                         userSwitcherRepository =
@@ -152,8 +149,8 @@
                                 userManager = userManager,
                                 userInfoController = userInfoController,
                                 userSwitcherController = userSwitcherControllerWrapper.controller,
-                            ),
-                    )
+                            )
+                    ),
             )
 
         // Collect the user switcher into currentUserSwitcher.
@@ -213,13 +210,12 @@
 
         val underTest =
             utils.footerActionsViewModel(
+                shadeMode = ShadeMode.Single,
                 footerActionsInteractor =
                     utils.footerActionsInteractor(
                         qsSecurityFooterUtils = qsSecurityFooterUtils,
                         securityRepository =
-                            utils.securityRepository(
-                                securityController = securityController,
-                            ),
+                            utils.securityRepository(securityController = securityController),
                     ),
             )
 
@@ -261,10 +257,7 @@
     fun foregroundServices() = runTest {
         val securityController = FakeSecurityController()
         val fgsManagerController =
-            FakeFgsManagerController(
-                showFooterDot = false,
-                numRunningPackages = 0,
-            )
+            FakeFgsManagerController(showFooterDot = false, numRunningPackages = 0)
         val qsSecurityFooterUtils = mock<QSSecurityFooterUtils>()
 
         // Mock QSSecurityFooter to map a SecurityModel into a SecurityButtonConfig using the
@@ -276,13 +269,11 @@
 
         val underTest =
             utils.footerActionsViewModel(
+                shadeMode = ShadeMode.Single,
                 footerActionsInteractor =
                     utils.footerActionsInteractor(
                         qsSecurityFooterUtils = qsSecurityFooterUtils,
-                        securityRepository =
-                            utils.securityRepository(
-                                securityController,
-                            ),
+                        securityRepository = utils.securityRepository(securityController),
                         foregroundServicesRepository =
                             utils.foregroundServicesRepository(fgsManagerController),
                     ),
@@ -340,14 +331,7 @@
 
         // Return a fake broadcastFlow that emits 3 fake events when collected.
         val broadcastFlow = flowOf(Unit, Unit, Unit)
-        whenever(
-                broadcastDispatcher.broadcastFlow(
-                    any(),
-                    nullable(),
-                    anyInt(),
-                    nullable(),
-                )
-            )
+        whenever(broadcastDispatcher.broadcastFlow(any(), nullable(), anyInt(), nullable()))
             .thenAnswer { broadcastFlow }
 
         // Increment nDialogRequests whenever a request to show the dialog is made by the
@@ -359,6 +343,7 @@
 
         val underTest =
             utils.footerActionsViewModel(
+                shadeMode = ShadeMode.Single,
                 footerActionsInteractor =
                     utils.footerActionsInteractor(
                         qsSecurityFooterUtils = qsSecurityFooterUtils,
@@ -376,7 +361,7 @@
 
     @Test
     fun alpha_inSplitShade_followsExpansion() {
-        val underTest = utils.footerActionsViewModel()
+        val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Split)
 
         underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true)
         assertThat(underTest.alpha.value).isEqualTo(0f)
@@ -396,7 +381,7 @@
 
     @Test
     fun backgroundAlpha_inSplitShade_followsExpansion_with_0_15_delay() {
-        val underTest = utils.footerActionsViewModel()
+        val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Split)
         val floatTolerance = 0.01f
 
         underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true)
@@ -420,7 +405,7 @@
 
     @Test
     fun alpha_inSingleShade_followsExpansion_with_0_9_delay() {
-        val underTest = utils.footerActionsViewModel()
+        val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Single)
         val floatTolerance = 0.01f
 
         underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = false)
@@ -444,7 +429,7 @@
 
     @Test
     fun backgroundAlpha_inSingleShade_always1() {
-        val underTest = utils.footerActionsViewModel()
+        val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Single)
 
         underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = false)
         assertThat(underTest.backgroundAlpha.value).isEqualTo(1f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt
index b591538..c775bfd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.systemui.qs.panels.domain.interactor
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -26,8 +24,9 @@
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
 import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
-import com.android.systemui.shade.data.repository.fakeShadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
+import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -40,29 +39,27 @@
 
     val Kosmos.underTest by Kosmos.Fixture { kosmos.gridLayoutTypeInteractor }
 
-    @DisableFlags(DualShade.FLAG_NAME)
     @Test
     fun noDualShade_gridAlwaysPaginated() =
         kosmos.runTest {
             val type by collectLastValue(underTest.layout)
 
-            fakeShadeRepository.setShadeLayoutWide(false)
+            kosmos.enableSingleShade()
             assertThat(type).isEqualTo(PaginatedGridLayoutType)
 
-            fakeShadeRepository.setShadeLayoutWide(true)
+            kosmos.enableSplitShade()
             assertThat(type).isEqualTo(PaginatedGridLayoutType)
         }
 
-    @EnableFlags(DualShade.FLAG_NAME)
     @Test
     fun dualShade_gridAlwaysInfinite() =
         kosmos.runTest {
             val type by collectLastValue(underTest.layout)
 
-            fakeShadeRepository.setShadeLayoutWide(false)
+            kosmos.enableDualShade(wideLayout = false)
             assertThat(type).isEqualTo(InfiniteGridLayoutType)
 
-            fakeShadeRepository.setShadeLayoutWide(true)
+            kosmos.enableDualShade(wideLayout = true)
             assertThat(type).isEqualTo(InfiniteGridLayoutType)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt
index 35f7504..2e7aeb4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt
@@ -17,8 +17,6 @@
 package com.android.systemui.qs.panels.domain.interactor
 
 import android.content.res.mainResources
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -29,8 +27,9 @@
 import com.android.systemui.qs.panels.data.repository.QSColumnsRepository
 import com.android.systemui.qs.panels.data.repository.qsColumnsRepository
 import com.android.systemui.res.R
-import com.android.systemui.shade.data.repository.fakeShadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
+import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -65,35 +64,36 @@
     }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun withSingleShade_returnsCorrectValue() =
         with(kosmos) {
             testScope.runTest {
                 val latest by collectLastValue(underTest.columns)
 
+                kosmos.enableSingleShade()
+
                 assertThat(latest).isEqualTo(1)
             }
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun withDualShade_returnsCorrectValue() =
         with(kosmos) {
             testScope.runTest {
                 val latest by collectLastValue(underTest.columns)
 
+                kosmos.enableDualShade()
+
                 assertThat(latest).isEqualTo(2)
             }
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun withSplitShade_returnsCorrectValue() =
         with(kosmos) {
             testScope.runTest {
                 val latest by collectLastValue(underTest.columns)
 
-                fakeShadeRepository.setShadeLayoutWide(true)
+                kosmos.enableSplitShade()
 
                 assertThat(latest).isEqualTo(3)
             }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt
index 635bada..e686d4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.flags.setFlagValue
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
@@ -30,8 +29,9 @@
 import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
-import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
+import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -57,7 +57,11 @@
 
     @Before
     fun setUp() {
-        mSetFlagsRule.setFlagValue(DualShade.FLAG_NAME, testData.shadeMode == ShadeMode.Dual)
+        when (testData.shadeMode) {
+            ShadeMode.Single -> kosmos.enableSingleShade()
+            ShadeMode.Split -> kosmos.enableSplitShade()
+            ShadeMode.Dual -> kosmos.enableDualShade()
+        }
     }
 
     @Test
@@ -66,7 +70,6 @@
             testScope.runTest {
                 underTest.activateIn(testScope)
 
-                shadeRepository.setShadeLayoutWide(testData.shadeMode != ShadeMode.Single)
                 val config =
                     Configuration(mainResources.configuration).apply {
                         orientation = testData.orientation
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt
index 4ae8589..241cdbf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt
@@ -17,8 +17,6 @@
 package com.android.systemui.qs.panels.ui.viewmodel
 
 import android.content.res.mainResources
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -37,7 +35,8 @@
 import com.android.systemui.qs.panels.data.repository.QSColumnsRepository
 import com.android.systemui.qs.panels.data.repository.qsColumnsRepository
 import com.android.systemui.res.R
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.domain.interactor.disableDualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -66,12 +65,12 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun mediaLocationNull_singleOrSplit_alwaysSingleShadeColumns() =
         with(kosmos) {
             testScope.runTest {
                 val underTest = qsColumnsViewModelFactory.create(null)
                 underTest.activateIn(testScope)
+                kosmos.disableDualShade()
 
                 setConfigurationForMediaInRow(mediaInRow = false)
 
@@ -89,12 +88,12 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun mediaLocationNull_dualShade_alwaysDualShadeColumns() =
         with(kosmos) {
             testScope.runTest {
                 val underTest = qsColumnsViewModelFactory.create(null)
                 underTest.activateIn(testScope)
+                kosmos.enableDualShade()
 
                 setConfigurationForMediaInRow(mediaInRow = false)
 
@@ -112,12 +111,12 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun mediaLocationQS_dualShade_alwaysDualShadeColumns() =
         with(kosmos) {
             testScope.runTest {
                 val underTest = qsColumnsViewModelFactory.create(LOCATION_QS)
                 underTest.activateIn(testScope)
+                kosmos.enableDualShade()
 
                 setConfigurationForMediaInRow(mediaInRow = false)
 
@@ -134,12 +133,12 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun mediaLocationQQS_dualShade_alwaysDualShadeColumns() =
         with(kosmos) {
             testScope.runTest {
                 val underTest = qsColumnsViewModelFactory.create(LOCATION_QQS)
                 underTest.activateIn(testScope)
+                kosmos.enableDualShade()
 
                 setConfigurationForMediaInRow(mediaInRow = false)
 
@@ -156,12 +155,12 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun mediaLocationQS_singleOrSplit_halfColumnsOnCorrectConfigurationAndVisible() =
         with(kosmos) {
             testScope.runTest {
                 val underTest = qsColumnsViewModelFactory.create(LOCATION_QS)
                 underTest.activateIn(testScope)
+                kosmos.disableDualShade()
 
                 setConfigurationForMediaInRow(mediaInRow = false)
                 runCurrent()
@@ -181,12 +180,12 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun mediaLocationQQS_singleOrSplit_halfColumnsOnCorrectConfigurationAndVisible() =
         with(kosmos) {
             testScope.runTest {
                 val underTest = qsColumnsViewModelFactory.create(LOCATION_QQS)
                 underTest.activateIn(testScope)
+                kosmos.disableDualShade()
 
                 setConfigurationForMediaInRow(mediaInRow = false)
                 runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index fecd8c3..4c834b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.qs.flags.QSComposeFragment
 import com.android.systemui.qs.flags.QsDetailedView
 import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager
 import com.android.systemui.qs.tiles.dialog.WifiStateWorker
 import com.android.systemui.res.R
@@ -109,6 +110,7 @@
     @Mock private lateinit var dialogManager: InternetDialogManager
     @Mock private lateinit var wifiStateWorker: WifiStateWorker
     @Mock private lateinit var accessPointController: AccessPointController
+    @Mock private lateinit var internetDetailsViewModelFactory: InternetDetailsViewModel.Factory
 
     @Before
     fun setUp() {
@@ -145,6 +147,7 @@
                 dialogManager,
                 wifiStateWorker,
                 accessPointController,
+                internetDetailsViewModelFactory
             )
 
         underTest.initialize()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index f005375..7bb28db 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -49,6 +49,7 @@
 import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
 import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
 import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
+import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.modesDialogViewModel
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.settings.FakeSettings
@@ -146,6 +147,7 @@
                 tileDataInteractor,
                 mapper,
                 userActionInteractor,
+                kosmos.modesDialogViewModel,
             )
 
         underTest.initialize()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
index a29289a..b5aaadc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
@@ -40,6 +40,7 @@
 import com.android.systemui.qs.tiles.impl.custom.customTileServiceInteractor
 import com.android.systemui.qs.tiles.impl.custom.customTileSpec
 import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.user.data.repository.userRepository
@@ -72,6 +73,7 @@
                 packageUpdatesRepository = customTilePackagesUpdatesRepository,
                 userRepository = userRepository,
                 tileScope = testScope.backgroundScope,
+                qsTileLogger = kosmos.qsTileLogger,
             )
         }
 
@@ -152,7 +154,7 @@
                     collectLastValue(
                         underTest.tileData(
                             TEST_USER_1.userHandle,
-                            flowOf(DataUpdateTrigger.InitialRequest)
+                            flowOf(DataUpdateTrigger.InitialRequest),
                         )
                     )
                 runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
index e4a9888..ce4a343 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
@@ -26,13 +26,13 @@
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.dialog.InternetDetailsContentManager
+import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager
 import com.android.systemui.qs.tiles.dialog.WifiStateWorker
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.statusbar.connectivity.AccessPointController
-import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.nullable
-import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -40,9 +40,10 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
 @SmallTest
@@ -54,15 +55,27 @@
 
     private lateinit var underTest: InternetTileUserActionInteractor
 
-    @Mock private lateinit var internetDialogManager: InternetDialogManager
-    @Mock private lateinit var wifiStateWorker: WifiStateWorker
-    @Mock private lateinit var controller: AccessPointController
+    private lateinit var internetDialogManager: InternetDialogManager
+    private lateinit var wifiStateWorker: WifiStateWorker
+    private lateinit var controller: AccessPointController
+    private lateinit var internetDetailsViewModelFactory: InternetDetailsViewModel.Factory
+    private lateinit var internetDetailsContentManagerFactory: InternetDetailsContentManager.Factory
+    private lateinit var internetDetailsViewModel: InternetDetailsViewModel
 
     @Before
     fun setup() {
         internetDialogManager = mock<InternetDialogManager>()
         wifiStateWorker = mock<WifiStateWorker>()
         controller = mock<AccessPointController>()
+        internetDetailsViewModelFactory = mock<InternetDetailsViewModel.Factory>()
+        internetDetailsContentManagerFactory = mock<InternetDetailsContentManager.Factory>()
+        internetDetailsViewModel =
+            InternetDetailsViewModel(
+                onLongClick = {},
+                accessPointController = mock<AccessPointController>(),
+                contentManagerFactory = internetDetailsContentManagerFactory,
+            )
+        whenever(internetDetailsViewModelFactory.create(any())).thenReturn(internetDetailsViewModel)
 
         underTest =
             InternetTileUserActionInteractor(
@@ -71,6 +84,7 @@
                 wifiStateWorker,
                 controller,
                 inputHandler,
+                internetDetailsViewModelFactory,
             )
     }
 
@@ -102,7 +116,7 @@
             underTest.handleInput(QSTileInputTestKtx.longClick(input))
 
             QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
-                Truth.assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS)
+                assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS)
             }
         }
 
@@ -114,7 +128,7 @@
             underTest.handleInput(QSTileInputTestKtx.longClick(input))
 
             QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
-                Truth.assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS)
+                assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS)
             }
         }
 
@@ -141,8 +155,7 @@
     @Test
     fun detailsViewModel() =
         kosmos.testScope.runTest {
-            assertThat(underTest.detailsViewModel.getTitle())
-                .isEqualTo("Internet")
+            assertThat(underTest.detailsViewModel.getTitle()).isEqualTo("Internet")
             assertThat(underTest.detailsViewModel.getSubTitle())
                 .isEqualTo("Tab a network to connect")
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
index 424afe1..e2d33b6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
@@ -107,7 +107,6 @@
     @Test
     fun addAndRemoveMedia_mediaVisibilityIsUpdated() =
         testScope.runTest {
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             val isMediaVisible by collectLastValue(underTest.isMediaVisible)
             val userMedia = MediaData(active = true)
 
@@ -125,7 +124,6 @@
     @Test
     fun addInactiveMedia_mediaVisibilityIsUpdated() =
         testScope.runTest {
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             val isMediaVisible by collectLastValue(underTest.isMediaVisible)
             val userMedia = MediaData(active = false)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
index 9396445..df2dd99 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -21,7 +21,9 @@
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.Back
 import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.UserActionResult.HideOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
@@ -53,7 +55,7 @@
             val actions by collectLastValue(underTest.actions)
             underTest.activateIn(this)
 
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
+            assertThat((actions?.get(Swipe.Up) as? HideOverlay)?.overlay)
                 .isEqualTo(Overlays.QuickSettingsShade)
             assertThat(actions?.get(Swipe.Down)).isNull()
         }
@@ -66,7 +68,7 @@
             underTest.activateIn(this)
             assertThat(isEditing).isFalse()
 
-            assertThat((actions?.get(Back) as? UserActionResult.HideOverlay)?.overlay)
+            assertThat((actions?.get(Back) as? HideOverlay)?.overlay)
                 .isEqualTo(Overlays.QuickSettingsShade)
         }
 
@@ -87,11 +89,11 @@
             val actions by collectLastValue(underTest.actions)
             underTest.activateIn(this)
 
-            assertThat(
-                    (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopLeft))
-                            as? UserActionResult.ReplaceByOverlay)
-                        ?.overlay
-                )
-                .isEqualTo(Overlays.NotificationsShade)
+            val action =
+                (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopLeft)) as? ShowOverlay)
+            assertThat(action?.overlay).isEqualTo(Overlays.NotificationsShade)
+            val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some
+            assertThat(overlaysToHide).isNotNull()
+            assertThat(overlaysToHide?.overlays).containsExactly(Overlays.QuickSettingsShade)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
index 7d366f6..01714d7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.qs.ui.viewmodel
 
-import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -36,9 +35,8 @@
 import com.android.systemui.scene.domain.startable.sceneContainerStartable
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.enableDualShade
 import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationScrollViewModel
@@ -57,7 +55,6 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @EnableSceneContainer
-@EnableFlags(DualShade.FLAG_NAME)
 class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() {
 
     private val kosmos =
@@ -72,6 +69,7 @@
     @Before
     fun setUp() {
         kosmos.sceneContainerStartable.start()
+        kosmos.enableDualShade()
         underTest.activateIn(testScope)
     }
 
@@ -131,7 +129,7 @@
     @Test
     fun showHeader_showsOnNarrowScreen() =
         testScope.runTest {
-            kosmos.shadeRepository.setShadeLayoutWide(false)
+            kosmos.enableDualShade(wideLayout = false)
             runCurrent()
 
             assertThat(underTest.showHeader).isTrue()
@@ -140,7 +138,7 @@
     @Test
     fun showHeader_hidesOnWideScreen() =
         testScope.runTest {
-            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.enableDualShade(wideLayout = true)
             runCurrent()
 
             assertThat(underTest.showHeader).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
index 9590816..707cd04 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
@@ -18,8 +18,6 @@
 
 package com.android.systemui.scene.domain.interactor
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
@@ -36,7 +34,8 @@
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.sceneDataSource
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.domain.interactor.disableDualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
 import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
@@ -72,9 +71,9 @@
     private val underTest by lazy { kosmos.sceneContainerOcclusionInteractor }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun invisibleDueToOcclusion_dualShadeDisabled() =
         testScope.runTest {
+            kosmos.disableDualShade()
             val invisibleDueToOcclusion by collectLastValue(underTest.invisibleDueToOcclusion)
             val keyguardState by collectLastValue(keyguardTransitionInteractor.currentKeyguardState)
 
@@ -134,9 +133,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun invisibleDueToOcclusion_dualShadeEnabled() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val invisibleDueToOcclusion by collectLastValue(underTest.invisibleDueToOcclusion)
             val keyguardState by collectLastValue(keyguardTransitionInteractor.currentKeyguardState)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index cb7267b..af30e43 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -103,8 +103,9 @@
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
+import com.android.systemui.shade.domain.interactor.disableDualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
 import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.shared.system.QuickStepContract
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
@@ -187,9 +188,9 @@
     }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun hydrateVisibility() =
         testScope.runTest {
+            kosmos.disableDualShade()
             val currentDesiredSceneKey by collectLastValue(sceneInteractor.currentScene)
             val isVisible by collectLastValue(sceneInteractor.isVisible)
             val transitionStateFlow =
@@ -248,9 +249,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun hydrateVisibility_dualShade() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentDesiredSceneKey by collectLastValue(sceneInteractor.currentScene)
             val currentDesiredOverlays by collectLastValue(sceneInteractor.currentOverlays)
             val isVisible by collectLastValue(sceneInteractor.isVisible)
@@ -1148,7 +1149,7 @@
 
     @Test
     @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
-    fun skipsFaceErrorHaptics_nonSfps_coEx() =
+    fun playsFaceErrorHaptics_nonSfps_coEx() =
         testScope.runTest {
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
@@ -1160,14 +1161,15 @@
             underTest.start()
             updateFaceAuthStatus(isSuccess = false)
 
-            assertThat(playErrorHaptic).isNull()
-            verify(vibratorHelper, never()).vibrateAuthError(anyString())
+            assertThat(playErrorHaptic).isNotNull()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            verify(vibratorHelper).vibrateAuthError(anyString())
             verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
         }
 
     @Test
     @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
-    fun skipsMSDLFaceErrorHaptics_nonSfps_coEx() =
+    fun playsMSDLFaceErrorHaptics_nonSfps_coEx() =
         testScope.runTest {
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
@@ -1179,9 +1181,10 @@
             underTest.start()
             updateFaceAuthStatus(isSuccess = false)
 
-            assertThat(playErrorHaptic).isNull()
-            assertThat(msdlPlayer.latestTokenPlayed).isNull()
-            assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+            assertThat(playErrorHaptic).isNotNull()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
+            assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
         }
 
     @Test
@@ -1739,9 +1742,9 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun hydrateInteractionState_whileLocked() =
         testScope.runTest {
+            kosmos.disableDualShade()
             val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen)
             underTest.start()
             runCurrent()
@@ -1826,9 +1829,9 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun hydrateInteractionState_whileUnlocked() =
         testScope.runTest {
+            kosmos.disableDualShade()
             val transitionStateFlow =
                 prepareState(
                     authenticationMethod = AuthenticationMethodModel.Pin,
@@ -1915,9 +1918,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun hydrateInteractionState_dualShade_whileLocked() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentDesiredOverlays by collectLastValue(sceneInteractor.currentOverlays)
             val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen)
             underTest.start()
@@ -2004,9 +2007,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun hydrateInteractionState_dualShade_whileUnlocked() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentDesiredOverlays by collectLastValue(sceneInteractor.currentOverlays)
             val transitionStateFlow =
                 prepareState(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
index fc915ca..048dd66 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.systemui.scene.ui.viewmodel
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -32,9 +30,10 @@
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
+import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -53,7 +52,6 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val shadeRepository by lazy { kosmos.shadeRepository }
     private lateinit var underTest: GoneUserActionsViewModel
 
     @Before
@@ -63,44 +61,40 @@
     }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun downTransitionKey_splitShadeEnabled_isGoneToSplitShade() =
         testScope.runTest {
             val userActions by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(true)
+            kosmos.enableSplitShade()
             runCurrent()
 
             assertThat(userActions?.get(Swipe.Down)?.transitionKey).isEqualTo(ToSplitShade)
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun downTransitionKey_splitShadeDisabled_isNull() =
         testScope.runTest {
             val userActions by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(false)
+            kosmos.enableSingleShade()
             runCurrent()
 
             assertThat(userActions?.get(Swipe.Down)?.transitionKey).isNull()
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun downTransitionKey_dualShadeEnabled_isNull() =
         testScope.runTest {
             val userActions by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(true)
+            kosmos.enableDualShade(wideLayout = true)
             runCurrent()
 
             assertThat(userActions?.get(Swipe.Down)?.transitionKey).isNull()
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun swipeDownWithTwoFingers_singleShade_goesToQuickSettings() =
         testScope.runTest {
             val userActions by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(false)
+            kosmos.enableSingleShade()
             runCurrent()
 
             assertThat(userActions?.get(swipeDownFromTopWithTwoFingers()))
@@ -108,11 +102,10 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun swipeDownWithTwoFingers_splitShade_goesToShade() =
         testScope.runTest {
             val userActions by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(true)
+            kosmos.enableSplitShade()
             runCurrent()
 
             assertThat(userActions?.get(swipeDownFromTopWithTwoFingers()))
@@ -120,10 +113,10 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun swipeDownWithTwoFingers_dualShadeEnabled_isNull() =
         testScope.runTest {
             val userActions by collectLastValue(underTest.actions)
+            kosmos.enableDualShade()
             runCurrent()
 
             assertThat(userActions?.get(swipeDownFromTopWithTwoFingers())).isNull()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index af895c8..399b48f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -18,8 +18,6 @@
 
 package com.android.systemui.scene.ui.viewmodel
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.view.MotionEvent
 import android.view.MotionEvent.ACTION_DOWN
 import android.view.MotionEvent.ACTION_OUTSIDE
@@ -41,9 +39,10 @@
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
-import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
+import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
 import com.android.systemui.testKosmos
@@ -69,7 +68,6 @@
     private val testScope by lazy { kosmos.testScope }
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
     private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
-    private val fakeShadeRepository by lazy { kosmos.fakeShadeRepository }
     private val sceneContainerConfig by lazy { kosmos.sceneContainerConfig }
     private val fakeRemoteInputRepository by lazy { kosmos.fakeRemoteInputRepository }
     private val falsingManager by lazy { kosmos.fakeFalsingManager }
@@ -324,44 +322,40 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun edgeDetector_singleShade_usesDefaultEdgeDetector() =
         testScope.runTest {
             val shadeMode by collectLastValue(kosmos.shadeInteractor.shadeMode)
-            fakeShadeRepository.setShadeLayoutWide(false)
-            assertThat(shadeMode).isEqualTo(ShadeMode.Single)
+            kosmos.enableSingleShade()
 
+            assertThat(shadeMode).isEqualTo(ShadeMode.Single)
             assertThat(underTest.edgeDetector).isEqualTo(DefaultEdgeDetector)
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun edgeDetector_splitShade_usesDefaultEdgeDetector() =
         testScope.runTest {
             val shadeMode by collectLastValue(kosmos.shadeInteractor.shadeMode)
-            fakeShadeRepository.setShadeLayoutWide(true)
-            assertThat(shadeMode).isEqualTo(ShadeMode.Split)
+            kosmos.enableSplitShade()
 
+            assertThat(shadeMode).isEqualTo(ShadeMode.Split)
             assertThat(underTest.edgeDetector).isEqualTo(DefaultEdgeDetector)
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun edgeDetector_dualShade_narrowScreen_usesSplitEdgeDetector() =
         testScope.runTest {
             val shadeMode by collectLastValue(kosmos.shadeInteractor.shadeMode)
-            fakeShadeRepository.setShadeLayoutWide(false)
+            kosmos.enableDualShade(wideLayout = false)
 
             assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
             assertThat(underTest.edgeDetector).isEqualTo(kosmos.splitEdgeDetector)
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun edgeDetector_dualShade_wideScreen_usesSplitEdgeDetector() =
         testScope.runTest {
             val shadeMode by collectLastValue(kosmos.shadeInteractor.shadeMode)
-            fakeShadeRepository.setShadeLayoutWide(true)
+            kosmos.enableDualShade(wideLayout = true)
 
             assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
             assertThat(underTest.edgeDetector).isEqualTo(kosmos.splitEdgeDetector)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index 27e9f07..3d5daf6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -318,7 +318,7 @@
             val displayId = 1
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = displayId))
             val onSaved = { _: Uri? -> }
-            focusedDisplayRepository.emit(displayId)
+            focusedDisplayRepository.setDisplayId(displayId)
 
             screenshotExecutor.executeScreenshots(
                 createScreenshotRequest(
@@ -345,7 +345,7 @@
                 display(TYPE_INTERNAL, id = Display.DEFAULT_DISPLAY),
                 display(TYPE_EXTERNAL, id = 1),
             )
-            focusedDisplayRepository.emit(5) // invalid display
+            focusedDisplayRepository.setDisplayId(5) // invalid display
             val onSaved = { _: Uri? -> }
             screenshotExecutor.executeScreenshots(
                 createScreenshotRequest(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index ee9cb14..93d1f59 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -20,7 +20,7 @@
 import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper.RunWithLooper
 import android.view.Choreographer
-import android.view.MotionEvent
+import android.view.accessibility.AccessibilityEvent
 import android.widget.FrameLayout
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -45,7 +45,10 @@
 import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository
 import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
+import com.android.systemui.shade.data.repository.ShadeAnimationRepository
+import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
 import com.android.systemui.statusbar.BlurUtils
 import com.android.systemui.statusbar.DragDownHelper
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -75,6 +78,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -185,6 +189,10 @@
                 notificationShadeDepthController,
                 underTest,
                 shadeViewController,
+                ShadeAnimationInteractorLegacyImpl(
+                    ShadeAnimationRepository(),
+                    ShadeRepositoryImpl(testScope),
+                ),
                 panelExpansionInteractor,
                 ShadeExpansionStateManager(),
                 notificationStackScrollLayoutController,
@@ -213,6 +221,7 @@
                 mock(),
                 { configurationForwarder },
                 brightnessMirrorShowingInteractor,
+                UnconfinedTestDispatcher(),
             )
 
         controller.setupExpandedStatusBar()
@@ -259,6 +268,20 @@
         verify(configurationForwarder).dispatchOnMovedToDisplay(eq(1), eq(config))
     }
 
+    @Test
+    @EnableFlags(AConfigFlags.FLAG_SHADE_LAUNCH_ACCESSIBILITY)
+    fun requestSendAccessibilityEvent_duringLaunchAnimation_blocksFocusEvent() {
+        underTest.setAnimatingContentLaunch(true)
+
+        assertThat(
+                underTest.requestSendAccessibilityEvent(
+                    underTest.getChildAt(0),
+                    AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED),
+                )
+            )
+            .isFalse()
+    }
+
     private fun captureInteractionEventHandler() {
         verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
         interactionEventHandler = interactionEventHandlerCaptor.value
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
index ab5fa8e..5566c10 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
@@ -38,7 +38,7 @@
 
     private val kosmos = testKosmos()
     private val insetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore
-    private val insetsProvider = insetsProviderStore.defaultDisplay
+    private val insetsProvider = insetsProviderStore.forDisplay(context.displayId)
 
     @JvmField @Rule val mockitoRule = MockitoJUnit.rule()!!
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/FocusShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/FocusShadeDisplayPolicyTest.kt
new file mode 100644
index 0000000..b4249ef
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/FocusShadeDisplayPolicyTest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.shade.display
+
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.shade.data.repository.fakeFocusedDisplayRepository
+import com.android.systemui.shade.data.repository.focusShadeDisplayPolicy
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FocusShadeDisplayPolicyTest : SysuiTestCase() {
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val testScope = kosmos.testScope
+    private val focusedDisplayRepository = kosmos.fakeFocusedDisplayRepository
+
+    private val underTest = kosmos.focusShadeDisplayPolicy
+
+    @Test
+    fun displayId_propagatedFromRepository() =
+        testScope.runTest {
+            val displayId by collectLastValue(underTest.displayId)
+
+            assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+
+            focusedDisplayRepository.setDisplayId(2)
+
+            assertThat(displayId).isEqualTo(2)
+
+            focusedDisplayRepository.setDisplayId(3)
+
+            assertThat(displayId).isEqualTo(3)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
index b1ec740..5c9cf82 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.systemui.shade.domain.interactor
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
@@ -34,7 +32,7 @@
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shadeTestUtil
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -596,11 +594,13 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun expandNotificationsShade_dualShade_opensOverlay() =
         testScope.runTest {
+            kosmos.enableDualShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).isEmpty()
 
@@ -611,12 +611,13 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun expandNotificationsShade_singleShade_switchesToShadeScene() =
         testScope.runTest {
-            shadeTestUtil.setSplitShade(false)
+            kosmos.enableSingleShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Single)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).isEmpty()
 
@@ -627,11 +628,14 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun expandNotificationsShade_dualShadeQuickSettingsOpen_replacesOverlay() =
         testScope.runTest {
+            kosmos.enableDualShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
+
             underTest.expandQuickSettingsShade("reason")
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade)
@@ -642,11 +646,13 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun expandQuickSettingsShade_dualShade_opensOverlay() =
         testScope.runTest {
+            kosmos.enableDualShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).isEmpty()
 
@@ -657,12 +663,13 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun expandQuickSettingsShade_singleShade_switchesToQuickSettingsScene() =
         testScope.runTest {
-            shadeTestUtil.setSplitShade(false)
+            kosmos.enableSingleShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Single)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).isEmpty()
 
@@ -673,12 +680,13 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun expandQuickSettingsShade_splitShade_switchesToShadeScene() =
         testScope.runTest {
-            shadeTestUtil.setSplitShade(true)
+            kosmos.enableSplitShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Split)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).isEmpty()
 
@@ -689,11 +697,14 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun expandQuickSettingsShade_dualShadeNotificationsOpen_replacesOverlay() =
         testScope.runTest {
+            kosmos.enableDualShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
+
             underTest.expandNotificationsShade("reason")
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade)
@@ -704,9 +715,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun collapseNotificationsShade_dualShade_hidesOverlay() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             openShade(Overlays.NotificationsShade)
@@ -718,26 +729,27 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun collapseNotificationsShade_singleShade_switchesToLockscreen() =
         testScope.runTest {
-            shadeTestUtil.setSplitShade(false)
+            kosmos.enableSingleShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Single)
+
             sceneInteractor.changeScene(Scenes.Shade, "reason")
             assertThat(currentScene).isEqualTo(Scenes.Shade)
             assertThat(currentOverlays).isEmpty()
 
             underTest.collapseNotificationsShade("reason")
-
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).isEmpty()
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun collapseQuickSettingsShade_dualShade_hidesOverlay() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             openShade(Overlays.QuickSettingsShade)
@@ -749,12 +761,14 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun collapseQuickSettingsShadeNotBypassingShade_singleShade_switchesToShade() =
         testScope.runTest {
-            shadeTestUtil.setSplitShade(false)
+            kosmos.enableSingleShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Single)
+
             sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
             assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
             assertThat(currentOverlays).isEmpty()
@@ -769,12 +783,14 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun collapseQuickSettingsShadeNotBypassingShade_splitShade_switchesToLockscreen() =
         testScope.runTest {
-            shadeTestUtil.setSplitShade(true)
+            kosmos.enableSplitShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Split)
+
             sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
             assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
             assertThat(currentOverlays).isEmpty()
@@ -789,12 +805,14 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun collapseQuickSettingsShadeBypassingShade_singleShade_switchesToLockscreen() =
         testScope.runTest {
-            shadeTestUtil.setSplitShade(false)
+            kosmos.enableSingleShade()
+            val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(shadeMode).isEqualTo(ShadeMode.Single)
+
             sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
             assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
             assertThat(currentOverlays).isEmpty()
@@ -809,9 +827,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun collapseEitherShade_dualShade_hidesBothOverlays() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             openShade(Overlays.QuickSettingsShade)
@@ -826,10 +844,13 @@
         }
 
     private fun TestScope.openShade(overlay: OverlayKey) {
+        val shadeMode by collectLastValue(kosmos.shadeModeInteractor.shadeMode)
         val isAnyExpanded by collectLastValue(underTest.isAnyExpanded)
         val currentScene by collectLastValue(sceneInteractor.currentScene)
         val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
         val initialScene = checkNotNull(currentScene)
+        assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
+
         sceneInteractor.showOverlay(overlay, "reason")
         kosmos.setSceneTransition(
             ObservableTransitionState.Idle(initialScene, checkNotNull(currentOverlays))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
index a47db2e..668f568 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
@@ -16,15 +16,11 @@
 
 package com.android.systemui.shade.domain.interactor
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -48,82 +44,79 @@
     }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun legacyShadeMode_narrowScreen_singleShade() =
         testScope.runTest {
             val shadeMode by collectLastValue(underTest.shadeMode)
-            kosmos.shadeRepository.setShadeLayoutWide(false)
+            kosmos.enableSingleShade()
 
             assertThat(shadeMode).isEqualTo(ShadeMode.Single)
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun legacyShadeMode_wideScreen_splitShade() =
         testScope.runTest {
             val shadeMode by collectLastValue(underTest.shadeMode)
-            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.enableSplitShade()
 
             assertThat(shadeMode).isEqualTo(ShadeMode.Split)
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun shadeMode_wideScreen_isDual() =
         testScope.runTest {
             val shadeMode by collectLastValue(underTest.shadeMode)
-            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.enableDualShade(wideLayout = true)
 
             assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun shadeMode_narrowScreen_isDual() =
         testScope.runTest {
             val shadeMode by collectLastValue(underTest.shadeMode)
-            kosmos.shadeRepository.setShadeLayoutWide(false)
+            kosmos.enableDualShade(wideLayout = false)
 
             assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
-    fun isDualShade_flagEnabled_true() =
+    fun isDualShade_settingEnabled_returnsTrue() =
         testScope.runTest {
-            // Initiate collection.
+            // TODO(b/391578667): Add a test case for user switching once the bug is fixed.
             val shadeMode by collectLastValue(underTest.shadeMode)
+            kosmos.enableDualShade()
 
+            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
             assertThat(underTest.isDualShade).isTrue()
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
-    fun isDualShade_flagDisabled_false() =
+    fun isDualShade_settingDisabled_returnsFalse() =
         testScope.runTest {
-            // Initiate collection.
             val shadeMode by collectLastValue(underTest.shadeMode)
+            kosmos.disableDualShade()
 
+            assertThat(shadeMode).isNotEqualTo(ShadeMode.Dual)
             assertThat(underTest.isDualShade).isFalse()
         }
 
     @Test
     fun getTopEdgeSplitFraction_narrowScreen_splitInHalf() =
         testScope.runTest {
-            // Ensure isShadeLayoutWide is collected.
-            val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
-            kosmos.shadeRepository.setShadeLayoutWide(false)
+            val shadeMode by collectLastValue(underTest.shadeMode)
+            kosmos.enableDualShade(wideLayout = false)
 
+            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
             assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
         }
 
     @Test
     fun getTopEdgeSplitFraction_wideScreen_splitInHalf() =
         testScope.runTest {
-            // Ensure isShadeLayoutWide is collected.
-            val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
-            kosmos.shadeRepository.setShadeLayoutWide(true)
+            val shadeMode by collectLastValue(underTest.shadeMode)
+            kosmos.enableDualShade(wideLayout = true)
 
+            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
             assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
index d37e0fb..0406c3e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.systemui.shade.domain.startable
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
@@ -39,8 +37,9 @@
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.shade.ShadeExpansionChangeEvent
 import com.android.systemui.shade.ShadeExpansionListener
+import com.android.systemui.shade.domain.interactor.disableDualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
 import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
 import com.android.systemui.statusbar.phone.scrimController
@@ -88,10 +87,10 @@
     }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun hydrateShadeMode_dualShadeDisabled() =
         testScope.runTest {
             overrideResource(R.bool.config_use_split_notification_shade, false)
+            kosmos.disableDualShade()
             val shadeMode by collectLastValue(shadeInteractor.shadeMode)
 
             underTest.start()
@@ -107,10 +106,10 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun hydrateShadeMode_dualShadeEnabled() =
         testScope.runTest {
             overrideResource(R.bool.config_use_split_notification_shade, false)
+            kosmos.enableDualShade()
             val shadeMode by collectLastValue(shadeInteractor.shadeMode)
 
             underTest.start()
@@ -159,11 +158,7 @@
 
             assertThat(latestChangeEvent)
                 .isEqualTo(
-                    ShadeExpansionChangeEvent(
-                        fraction = 0f,
-                        expanded = false,
-                        tracking = false,
-                    )
+                    ShadeExpansionChangeEvent(fraction = 0f, expanded = false, tracking = false)
                 )
 
             changeScene(Scenes.Shade, transitionState) { progress ->
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index 0da1e7f..8ce20d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -1,8 +1,6 @@
 package com.android.systemui.shade.ui.viewmodel
 
 import android.content.Intent
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.provider.AlarmClock
 import android.provider.Settings
 import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
@@ -23,7 +21,8 @@
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.domain.interactor.disableDualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor
 import com.android.systemui.testKosmos
@@ -101,9 +100,9 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun onSystemIconChipClicked_locked_collapsesShadeToLockscreen() =
         testScope.runTest {
+            kosmos.disableDualShade()
             setDeviceEntered(false)
             setScene(Scenes.Shade)
 
@@ -114,9 +113,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun onSystemIconChipClicked_lockedOnQsShade_collapsesShadeToLockscreen() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             setDeviceEntered(false)
@@ -132,9 +131,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun onSystemIconChipClicked_lockedOnNotifShade_expandsQsShade() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             setDeviceEntered(false)
@@ -151,9 +150,9 @@
         }
 
     @Test
-    @DisableFlags(DualShade.FLAG_NAME)
     fun onSystemIconChipClicked_unlocked_collapsesShadeToGone() =
         testScope.runTest {
+            kosmos.disableDualShade()
             setDeviceEntered(true)
             setScene(Scenes.Shade)
 
@@ -164,9 +163,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun onSystemIconChipClicked_unlockedOnQsShade_collapsesShadeToGone() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             setDeviceEntered(true)
@@ -182,9 +181,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun onSystemIconChipClicked_unlockedOnNotifShade_expandsQsShade() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             setDeviceEntered(true)
@@ -201,9 +200,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun onNotificationIconChipClicked_lockedOnNotifShade_collapsesShadeToLockscreen() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             setDeviceEntered(false)
@@ -219,9 +218,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun onNotificationIconChipClicked_lockedOnQsShade_expandsNotifShade() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             setDeviceEntered(false)
@@ -238,9 +237,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun onNotificationIconChipClicked_unlockedOnNotifShade_collapsesShadeToGone() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             setDeviceEntered(true)
@@ -256,9 +255,9 @@
         }
 
     @Test
-    @EnableFlags(DualShade.FLAG_NAME)
     fun onNotificationIconChipClicked_unlockedOnQsShade_expandsNotifShade() =
         testScope.runTest {
+            kosmos.enableDualShade()
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             setDeviceEntered(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt
index a9d5790..27faeb8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.shade.ui.viewmodel
 
 import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
-import android.platform.test.annotations.DisableFlags
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -36,12 +35,11 @@
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.media.controls.data.repository.mediaFilterRepository
 import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.qs.ui.adapter.fakeQSSceneAdapter
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.domain.interactor.disableDualShade
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
 import com.android.systemui.testKosmos
@@ -64,20 +62,19 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @EnableSceneContainer
-@DisableFlags(DualShade.FLAG_NAME)
 class ShadeSceneContentViewModelTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
     private val shadeRepository by lazy { kosmos.shadeRepository }
-    private val qsSceneAdapter by lazy { kosmos.fakeQSSceneAdapter }
 
     private val underTest: ShadeSceneContentViewModel by lazy { kosmos.shadeSceneContentViewModel }
 
     @Before
     fun setUp() {
         underTest.activateIn(testScope)
+        kosmos.disableDualShade()
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
index bbfc66a..8f904f7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.shade.ui.viewmodel
 
-import android.platform.test.annotations.DisableFlags
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -39,16 +38,16 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.qs.ui.adapter.fakeQSSceneAdapter
-import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
 import com.android.systemui.scene.domain.startable.sceneContainerStartable
 import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.disableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
+import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.shade.domain.startable.shadeStartable
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -66,13 +65,11 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @EnableSceneContainer
-@DisableFlags(DualShade.FLAG_NAME)
 class ShadeUserActionsViewModelTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
-    private val shadeRepository by lazy { kosmos.shadeRepository }
     private val qsSceneAdapter by lazy { kosmos.fakeQSSceneAdapter }
 
     private val underTest: ShadeUserActionsViewModel by lazy { kosmos.shadeUserActionsViewModel }
@@ -80,6 +77,7 @@
     @Before
     fun setUp() {
         kosmos.sceneContainerStartable.start()
+        kosmos.disableDualShade()
         underTest.activateIn(testScope)
     }
 
@@ -164,7 +162,7 @@
     fun upTransitionKey_splitShadeEnabled_isGoneToSplitShade() =
         testScope.runTest {
             val actions by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(true)
+            kosmos.enableSplitShade()
             runCurrent()
 
             assertThat(actions?.get(Swipe.Up)?.transitionKey).isEqualTo(ToSplitShade)
@@ -174,7 +172,7 @@
     fun upTransitionKey_splitShadeDisable_isNull() =
         testScope.runTest {
             val actions by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(false)
+            kosmos.enableSingleShade()
             runCurrent()
 
             assertThat(actions?.get(Swipe.Up)?.transitionKey).isNull()
@@ -183,7 +181,7 @@
     @Test
     fun downTransitionSceneKey_inSplitShade_null() =
         testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, true)
+            kosmos.enableSplitShade()
             kosmos.shadeStartable.start()
             val actions by collectLastValue(underTest.actions)
             assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
@@ -193,7 +191,7 @@
     @Test
     fun downTransitionSceneKey_notSplitShade_quickSettings() =
         testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, false)
+            kosmos.enableSingleShade()
             kosmos.shadeStartable.start()
             val actions by collectLastValue(underTest.actions)
             assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 2020d0d..3d31787 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -460,17 +460,17 @@
     }
 
     @Test
-    public void testOnDisplayReady() {
-        mCommandQueue.onDisplayReady(DEFAULT_DISPLAY);
+    public void testonDisplayAddSystemDecorations() {
+        mCommandQueue.onDisplayAddSystemDecorations(DEFAULT_DISPLAY);
         waitForIdleSync();
-        verify(mCallbacks).onDisplayReady(eq(DEFAULT_DISPLAY));
+        verify(mCallbacks).onDisplayAddSystemDecorations(eq(DEFAULT_DISPLAY));
     }
 
     @Test
-    public void testOnDisplayReadyForSecondaryDisplay() {
-        mCommandQueue.onDisplayReady(SECONDARY_DISPLAY);
+    public void testonDisplayAddSystemDecorationsForSecondaryDisplay() {
+        mCommandQueue.onDisplayAddSystemDecorations(SECONDARY_DISPLAY);
         waitForIdleSync();
-        verify(mCallbacks).onDisplayReady(eq(SECONDARY_DISPLAY));
+        verify(mCallbacks).onDisplayAddSystemDecorations(eq(SECONDARY_DISPLAY));
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 0713a24..baaf6c9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -84,7 +84,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.log.LogWtfHandlerRule;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.NotificationStateChangedListener;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -156,7 +156,7 @@
     @Mock
     private NotificationClickNotifier mClickNotifier;
     @Mock
-    private OverviewProxyService mOverviewProxyService;
+    private LauncherProxyService mLauncherProxyService;
     @Mock
     private KeyguardManager mKeyguardManager;
     @Mock
@@ -1142,7 +1142,7 @@
                     (() -> mVisibilityProvider),
                     (() -> mNotifCollection),
                     mClickNotifier,
-                    (() -> mOverviewProxyService),
+                    (() -> mLauncherProxyService),
                     NotificationLockscreenUserManagerTest.this.mKeyguardManager,
                     mStatusBarStateController,
                     mMainExecutor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 0a05649..a79f408 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
 import com.android.wm.shell.appzoomout.AppZoomOut
 import com.google.common.truth.Truth.assertThat
+import java.util.Optional
 import java.util.function.Consumer
 import org.junit.Before
 import org.junit.Rule
@@ -66,7 +67,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
-import java.util.Optional
 
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
@@ -150,24 +150,23 @@
 
     @Test
     fun setupListeners() {
-        verify(dumpManager).registerCriticalDumpable(
-            anyString(), eq(notificationShadeDepthController)
-        )
+        verify(dumpManager)
+            .registerCriticalDumpable(anyString(), eq(notificationShadeDepthController))
     }
 
     @Test
     fun onPanelExpansionChanged_apliesBlur_ifShade() {
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+        )
         verify(shadeAnimation).animateTo(eq(maxBlur))
     }
 
     @Test
     fun onPanelExpansionChanged_animatesBlurIn_ifShade() {
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 0.01f, expanded = false, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 0.01f, expanded = false, tracking = false)
+        )
         verify(shadeAnimation).animateTo(eq(maxBlur))
     }
 
@@ -176,27 +175,27 @@
         onPanelExpansionChanged_animatesBlurIn_ifShade()
         clearInvocations(shadeAnimation)
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 0f, expanded = false, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 0f, expanded = false, tracking = false)
+        )
         verify(shadeAnimation).animateTo(eq(0))
     }
 
     @Test
     fun onPanelExpansionChanged_animatesBlurOut_ifFlick() {
-        val event =
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false)
+        val event = ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
         onPanelExpansionChanged_apliesBlur_ifShade()
         clearInvocations(shadeAnimation)
         notificationShadeDepthController.onPanelExpansionChanged(event)
         verify(shadeAnimation, never()).animateTo(anyInt())
 
         notificationShadeDepthController.onPanelExpansionChanged(
-            event.copy(fraction = 0.9f, tracking = true))
+            event.copy(fraction = 0.9f, tracking = true)
+        )
         verify(shadeAnimation, never()).animateTo(anyInt())
 
         notificationShadeDepthController.onPanelExpansionChanged(
-            event.copy(fraction = 0.8f, tracking = false))
+            event.copy(fraction = 0.8f, tracking = false)
+        )
         verify(shadeAnimation).animateTo(eq(0))
     }
 
@@ -205,16 +204,14 @@
         onPanelExpansionChanged_animatesBlurOut_ifFlick()
         clearInvocations(shadeAnimation)
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 0.6f, expanded = true, tracking = true))
+            ShadeExpansionChangeEvent(fraction = 0.6f, expanded = true, tracking = true)
+        )
         verify(shadeAnimation).animateTo(eq(maxBlur))
     }
 
     @Test
     fun onPanelExpansionChanged_respectsMinPanelPullDownFraction() {
-        val event =
-            ShadeExpansionChangeEvent(
-                fraction = 0.5f, expanded = true, tracking = true)
+        val event = ShadeExpansionChangeEvent(fraction = 0.5f, expanded = true, tracking = true)
         notificationShadeDepthController.panelPullDownMinFraction = 0.5f
         notificationShadeDepthController.onPanelExpansionChanged(event)
         assertThat(notificationShadeDepthController.shadeExpansion).isEqualTo(0f)
@@ -241,8 +238,8 @@
         statusBarState = StatusBarState.KEYGUARD
         notificationShadeDepthController.qsPanelExpansion = 1f
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+        )
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
     }
@@ -252,8 +249,8 @@
         statusBarState = StatusBarState.KEYGUARD
         notificationShadeDepthController.qsPanelExpansion = 0.25f
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+        )
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(wallpaperController)
             .setNotificationShadeZoom(eq(ShadeInterpolation.getNotificationScrimAlpha(0.25f)))
@@ -264,8 +261,8 @@
         enableSplitShade()
 
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+        )
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
 
         verify(wallpaperController).setNotificationShadeZoom(0f)
@@ -276,8 +273,8 @@
         disableSplitShade()
 
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+        )
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
 
         verify(wallpaperController).setNotificationShadeZoom(floatThat { it > 0 })
@@ -354,8 +351,8 @@
     @Test
     fun updateBlurCallback_setsBlur_whenExpanded() {
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+        )
         `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
@@ -364,8 +361,8 @@
     @Test
     fun updateBlurCallback_ignoreShadeBlurUntilHidden_overridesZoom() {
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+        )
         `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
         notificationShadeDepthController.blursDisabledForAppLaunch = true
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
@@ -373,7 +370,7 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_BOUNCER_UI_REVAMP)
+    @DisableFlags(Flags.FLAG_BOUNCER_UI_REVAMP, Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
     fun ignoreShadeBlurUntilHidden_schedulesFrame() {
         notificationShadeDepthController.blursDisabledForAppLaunch = true
         verify(blurUtils).prepareBlur(any(), anyInt())
@@ -391,8 +388,8 @@
     @Test
     fun ignoreBlurForUnlock_ignores() {
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+        )
         `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
 
         notificationShadeDepthController.blursDisabledForAppLaunch = false
@@ -408,8 +405,8 @@
     @Test
     fun ignoreBlurForUnlock_doesNotIgnore() {
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+        )
         `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
 
         notificationShadeDepthController.blursDisabledForAppLaunch = false
@@ -435,14 +432,14 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_BOUNCER_UI_REVAMP)
+    @DisableFlags(Flags.FLAG_BOUNCER_UI_REVAMP, Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
     fun brightnessMirror_hidesShadeBlur() {
         // Brightness mirror is fully visible
         `when`(brightnessSpring.ratio).thenReturn(1f)
         // And shade is blurred
         notificationShadeDepthController.onPanelExpansionChanged(
-            ShadeExpansionChangeEvent(
-                fraction = 1f, expanded = true, tracking = false))
+            ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+        )
         `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
 
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index f8720b4..a51e0c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -19,8 +19,6 @@
 package com.android.systemui.statusbar
 
 import android.animation.ObjectAnimator
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
@@ -53,8 +51,9 @@
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.disableDualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
 import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
 import com.android.systemui.testKosmos
 import com.android.systemui.util.kotlin.JavaAdapter
@@ -246,9 +245,9 @@
 
     @Test
     @EnableSceneContainer
-    @DisableFlags(DualShade.FLAG_NAME)
     fun start_hydratesStatusBarState_whileLocked() =
         testScope.runTest {
+            kosmos.disableDualShade()
             var statusBarState = underTest.state
             val listener =
                 object : StatusBarStateController.StateListener {
@@ -303,9 +302,9 @@
 
     @Test
     @EnableSceneContainer
-    @DisableFlags(DualShade.FLAG_NAME)
     fun start_hydratesStatusBarState_withAlternateBouncer() =
         testScope.runTest {
+            kosmos.disableDualShade()
             var statusBarState = underTest.state
             val listener =
                 object : StatusBarStateController.StateListener {
@@ -349,9 +348,9 @@
 
     @Test
     @EnableSceneContainer
-    @EnableFlags(DualShade.FLAG_NAME)
     fun start_hydratesStatusBarState_dualShade_whileLocked() =
         testScope.runTest {
+            kosmos.enableDualShade()
             var statusBarState = underTest.state
             val listener =
                 object : StatusBarStateController.StateListener {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
index dea3d1f..0dc2759 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
@@ -22,21 +22,26 @@
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
 import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
+import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mockito.doAnswer
 import org.mockito.kotlin.any
@@ -44,8 +49,9 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class MediaProjectionChipInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val testScope = kosmos.testScope
     private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
 
@@ -57,6 +63,26 @@
     private val underTest = kosmos.mediaProjectionChipInteractor
 
     @Test
+    fun projectionStartedDuringCallAndActivePostCallEvent_eventEmitted_isUnit() =
+        kosmos.runTest {
+            val latest by
+                collectLastValue(underTest.projectionStartedDuringCallAndActivePostCallEvent)
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            assertThat(latest).isEqualTo(Unit)
+        }
+
+    @Test
+    fun projectionStartedDuringCallAndActivePostCallEvent_noEventEmitted_isNull() =
+        kosmos.runTest {
+            val latest by
+                collectLastValue(underTest.projectionStartedDuringCallAndActivePostCallEvent)
+
+            assertThat(latest).isNull()
+        }
+
+    @Test
     fun projection_notProjectingState_isNotProjecting() =
         testScope.runTest {
             val latest by collectLastValue(underTest.projection)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index eec23d3..942e6554 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.core.StatusBarRootModernization
 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
 import com.android.systemui.statusbar.notification.data.repository.UnconfinedFakeHeadsUpRowRepository
@@ -44,6 +45,7 @@
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -609,7 +611,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -629,22 +631,26 @@
         }
 
     @Test
-    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
-    fun chips_clickingChipNotifiesInteractor() =
+    @DisableFlags(
+        FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY,
+        StatusBarRootModernization.FLAG_NAME,
+        StatusBarChipsModernization.FLAG_NAME,
+    )
+    fun chips_chipsModernizationDisabled_clickingChipNotifiesInteractor() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
-            val latestChipTap by
+            val latestChipTapKey by
                 collectLastValue(
                     kosmos.statusBarNotificationChipsInteractor.promotedNotificationChipTapEvent
                 )
+            val key = "clickTest"
 
             setNotifs(
                 listOf(
                     activeNotificationModel(
-                        key = "clickTest",
+                        key,
                         statusBarChipIcon = createStatusBarIconViewOrNull(),
-                        promotedContent =
-                            PromotedNotificationContentModel.Builder("clickTest").build(),
+                        promotedContent = PromotedNotificationContentModel.Builder(key).build(),
                     )
                 )
             )
@@ -652,7 +658,41 @@
 
             chip.onClickListenerLegacy!!.onClick(mock<View>())
 
-            assertThat(latestChipTap).isEqualTo("clickTest")
+            assertThat(latestChipTapKey).isEqualTo(key)
+        }
+
+    @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+    @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+    fun chips_chipsModernizationEnabled_clickingChipNotifiesInteractor() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chips)
+            val latestChipTapKey by
+                collectLastValue(
+                    kosmos.statusBarNotificationChipsInteractor.promotedNotificationChipTapEvent
+                )
+            val key = "clickTest"
+
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key,
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
+                        promotedContent = PromotedNotificationContentModel.Builder(key).build(),
+                    )
+                )
+            )
+            val chip = latest!![0]
+
+            assertThat(chip.clickBehavior)
+                .isInstanceOf(
+                    OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification::class.java
+                )
+
+            (chip.clickBehavior as OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification)
+                .onClick()
+
+            assertThat(latestChipTapKey).isEqualTo(key)
         }
 
     private fun setNotifs(notifs: List<ActiveNotificationModel>) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index 36fc5aa..5a66888 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -31,9 +31,10 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -41,6 +42,7 @@
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.CAST_TO_OTHER_DEVICES_PACKAGE
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel
 import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndGenericShareToAppDialogDelegate
 import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndShareScreenToAppDialogDelegate
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
@@ -51,6 +53,7 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
 import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
+import com.android.systemui.testKosmos
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -58,6 +61,7 @@
 import org.junit.Before
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mockito.times
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.eq
@@ -68,7 +72,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ShareToAppChipViewModelTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val testScope = kosmos.testScope
     private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
     private val systemClock = kosmos.fakeSystemClock
@@ -89,9 +93,11 @@
         mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }
 
     private val underTest = kosmos.shareToAppChipViewModel
+    private val mockDialog = mock<SystemUIDialog>()
 
     @Before
     fun setUp() {
+        underTest.start()
         setUpPackageManagerForMediaProjection(kosmos)
 
         whenever(kosmos.mockSystemUIDialogFactory.create(any<EndShareScreenToAppDialogDelegate>()))
@@ -101,6 +107,196 @@
     }
 
     @Test
+    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun chip_flagEnabled_projectionStartedDuringCallAndActivePostCallEventEmitted_chipHidden() =
+        kosmos.runTest {
+            val latestChip by collectLastValue(underTest.chip)
+
+            // Set mediaProjectionState to Projecting
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            // Verify the chip is initially shown
+            assertThat(latestChip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            // Verify the chip is hidden
+            assertThat(latestChip).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    @DisableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun chip_flagDisabled_projectionStartedDuringCallAndActivePostCallEventEmitted_chipRemainsVisible() =
+        kosmos.runTest {
+            val latestChip by collectLastValue(underTest.chip)
+
+            // Set mediaProjectionState to Projecting
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            // Verify the chip is initially shown
+            assertThat(latestChip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            // Chip is still shown
+            assertThat(latestChip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+        }
+
+    @Test
+    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun stopDialog_flagEnabled_initialState_isHidden() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.stopDialogToShow)
+
+            assertThat(latest).isEqualTo(MediaProjectionStopDialogModel.Hidden)
+        }
+
+    @Test
+    @DisableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun stopDialog_flagDisabled_projectionStartedDuringCallAndActivePostCallEventEmitted_dialogRemainsHidden() =
+        kosmos.runTest {
+            val latestStopDialogModel by collectLastValue(underTest.stopDialogToShow)
+
+            // Set mediaProjectionRepo state to Projecting
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            // Verify that no dialog is shown
+            assertThat(latestStopDialogModel).isEqualTo(MediaProjectionStopDialogModel.Hidden)
+        }
+
+    @Test
+    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun stopDialog_notProjectingState_flagEnabled_remainsHidden() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.stopDialogToShow)
+
+            // Set the state to not projecting
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            // Verify that the dialog remains hidden
+            assertThat(latest).isEqualTo(MediaProjectionStopDialogModel.Hidden)
+        }
+
+    @Test
+    @EnableFlags(
+        com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END,
+        FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP,
+    )
+    fun stopDialog_projectingAudio_flagEnabled_eventEmitted_showsGenericStopDialog() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.stopDialogToShow)
+
+            // Set the state to projecting audio
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE)
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            // Verify that the generic dialog is shown
+            assertThat(latest).isInstanceOf(MediaProjectionStopDialogModel.Shown::class.java)
+            val dialogDelegate = (latest as MediaProjectionStopDialogModel.Shown).dialogDelegate
+            assertThat(dialogDelegate).isInstanceOf(EndGenericShareToAppDialogDelegate::class.java)
+        }
+
+    @Test
+    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun stopDialog_projectingEntireScreen_flagEnabled_eventEmitted_showsShareScreenToAppStopDialog() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.stopDialogToShow)
+
+            // Set the state to projecting the entire screen
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            assertThat(latest).isInstanceOf(MediaProjectionStopDialogModel.Hidden::class.java)
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            // Verify that the dialog is shown
+            assertThat(latest).isInstanceOf(MediaProjectionStopDialogModel.Shown::class.java)
+            val dialogDelegate = (latest as MediaProjectionStopDialogModel.Shown).dialogDelegate
+            assertThat(dialogDelegate).isInstanceOf(EndShareScreenToAppDialogDelegate::class.java)
+        }
+
+    @Test
+    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun stopDialog_projectingEntireScreen_eventEmitted_hasCancelBehaviour() =
+        kosmos.runTest {
+            val latestDialogModel by collectLastValue(underTest.stopDialogToShow)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            // Verify that the dialog is shown
+            assertThat(latestDialogModel)
+                .isInstanceOf(MediaProjectionStopDialogModel.Shown::class.java)
+
+            val dialogModel = latestDialogModel as MediaProjectionStopDialogModel.Shown
+
+            whenever(dialogModel.dialogDelegate.createDialog()).thenReturn(mockDialog)
+
+            dialogModel.createAndShowDialog()
+
+            // Verify dialog is shown
+            verify(mockDialog).show()
+
+            // Verify dialog is hidden when dialog is cancelled
+            argumentCaptor<DialogInterface.OnCancelListener>().apply {
+                verify(mockDialog).setOnCancelListener(capture())
+                firstValue.onCancel(mockDialog)
+            }
+            assertThat(underTest.stopDialogToShow.value)
+                .isEqualTo(MediaProjectionStopDialogModel.Hidden)
+
+            verify(mockDialog, times(1)).setOnCancelListener(any())
+        }
+
+    @Test
+    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun stopDialog_projectingEntireScreen_eventEmitted_hasDismissBehaviour() =
+        kosmos.runTest {
+            val latestDialogModel by collectLastValue(underTest.stopDialogToShow)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            // Verify that the dialog is shown
+            assertThat(latestDialogModel)
+                .isInstanceOf(MediaProjectionStopDialogModel.Shown::class.java)
+
+            val dialogModel = latestDialogModel as MediaProjectionStopDialogModel.Shown
+
+            whenever(dialogModel.dialogDelegate.createDialog()).thenReturn(mockDialog)
+
+            // Simulate showing the dialog
+            dialogModel.createAndShowDialog()
+
+            // Verify dialog is shown
+            verify(mockDialog).show()
+
+            // Verify dialog is hidden when dialog is dismissed
+            argumentCaptor<DialogInterface.OnDismissListener>().apply {
+                verify(mockDialog).setOnDismissListener(capture())
+                firstValue.onDismiss(mockDialog)
+            }
+            assertThat(underTest.stopDialogToShow.value)
+                .isEqualTo(MediaProjectionStopDialogModel.Hidden)
+
+            verify(mockDialog, times(1)).setOnDismissListener(any())
+        }
+
+    @Test
     fun chip_notProjectingState_isHidden() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index a4b6a84..7a33cbe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -289,7 +289,11 @@
     fun primaryChip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
-            mediaProjectionState.value = MediaProjectionState.NotProjecting
+            mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(
+                    NORMAL_PACKAGE,
+                    hostDeviceName = "Recording Display",
+                )
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
             val latest by collectLastValue(underTest.primaryChip)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
index 22906b8..c515d94 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
@@ -144,7 +144,8 @@
                 /* rankingAdjustment = */ 0,
                 /* isBubble = */ false,
                 /* proposedImportance = */ 0,
-                /* sensitiveContent = */ false
+                /* sensitiveContent = */ false,
+                /* summarization = */ null
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 7b12094..2aa1efa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -107,7 +107,7 @@
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getParams() {
         return SceneContainerFlagParameterizationKt
-                .andSceneContainer(allCombinationsOf(Flags.FLAG_STABILIZE_HEADS_UP_GROUP));
+                .andSceneContainer(allCombinationsOf(Flags.FLAG_STABILIZE_HEADS_UP_GROUP_V2));
     }
 
     private VisualStabilityCoordinator mCoordinator;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index 3c772fd..356eedb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
+import com.android.systemui.statusbar.RankingBuilder
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
@@ -242,4 +243,42 @@
         // Then: need no re-inflation
         assertFalse(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
     }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NM_SUMMARIZATION_UI)
+    fun changeIsSummarization_needReInflation_newlySummarized() {
+        // Given: an Entry with no summarization
+        val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
+        assertThat(oldAdjustment.summarization).isNull()
+
+        // When: the Entry now has a summarization
+        val rb = RankingBuilder(entry.ranking)
+        rb.setSummarization("summary!")
+        entry.ranking = rb.build()
+        val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
+        assertThat(newAdjustment).isNotEqualTo(oldAdjustment)
+
+        // Then: Need re-inflation
+        assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NM_SUMMARIZATION_UI)
+    fun changeIsSummarization_needReInflation_summarizationChanged() {
+        // Given: an Entry with no summarization
+        val rb = RankingBuilder(entry.ranking)
+        rb.setSummarization("summary!")
+        entry.ranking = rb.build()
+        val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
+
+        // When: the Entry now has a new summarization
+        val rb2 = RankingBuilder(entry.ranking)
+        rb2.setSummarization("summary new!")
+        entry.ranking = rb2.build()
+        val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
+        assertThat(newAdjustment).isNotEqualTo(oldAdjustment)
+
+        // Then: Need re-inflation
+        assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
index 9a42f5b..163ae47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
@@ -18,6 +18,8 @@
 
 import android.app.Flags
 import android.app.NotificationManager.Policy
+import android.content.res.Configuration
+import android.os.LocaleList
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
@@ -27,6 +29,7 @@
 import com.android.settingslib.notification.data.repository.updateNotificationPolicy
 import com.android.settingslib.notification.modes.TestModeBuilder
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.kosmos.testScope
@@ -36,9 +39,12 @@
 import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
+import java.util.Locale
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
@@ -53,6 +59,10 @@
     private val zenModeRepository = kosmos.zenModeRepository
     private val activeNotificationListRepository = kosmos.activeNotificationListRepository
     private val fakeSecureSettingsRepository = kosmos.fakeSecureSettingsRepository
+    private val fakeConfigurationRepository = kosmos.fakeConfigurationRepository
+
+    /** Backup of the current locales, to be restored at the end of the test if they are changed. */
+    private lateinit var originalLocales: LocaleList
 
     private val underTest by lazy { kosmos.emptyShadeViewModel }
 
@@ -68,6 +78,18 @@
         mSetFlagsRule.setFlagsParameterization(flags)
     }
 
+    @Before
+    fun setUp() {
+        originalLocales = context.resources.configuration.locales
+        updateLocales(LocaleList(Locale.US))
+    }
+
+    @After
+    fun tearDown() {
+        // Make sure we restore the original locale even if a test fails after changing it
+        updateLocales(originalLocales)
+    }
+
     @Test
     fun areNotificationsHiddenInShade_true() =
         testScope.runTest {
@@ -144,6 +166,29 @@
 
     @Test
     @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
+    fun text_changesWhenLocaleChanges() =
+        testScope.runTest {
+            val text by collectLastValue(underTest.text)
+
+            zenModeRepository.updateNotificationPolicy(
+                suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
+            )
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_OFF)
+            runCurrent()
+
+            assertThat(text).isEqualTo("No notifications")
+
+            updateLocales(LocaleList(Locale.GERMAN))
+            runCurrent()
+
+            assertThat(text).isEqualTo("Keine Benachrichtigungen")
+
+            // Make sure we restore the original locales
+            updateLocales(originalLocales)
+        }
+
+    @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
     fun text_reflectsModesHidingNotifications() =
         testScope.runTest {
             val text by collectLastValue(underTest.text)
@@ -285,4 +330,11 @@
             assertThat(onClick?.targetIntent?.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
             assertThat(onClick?.backStack).isEmpty()
         }
+
+    private fun updateLocales(locales: LocaleList) {
+        val configuration = Configuration()
+        configuration.setLocales(locales)
+        context.resources.updateConfiguration(configuration, context.resources.displayMetrics)
+        fakeConfigurationRepository.onConfigurationChange(configuration)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index 8d90d38..abb1edf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -173,6 +173,17 @@
 
     @Test
     @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+    fun extractsContent_fromBaseStyle() {
+        val entry = createEntry { setStyle(null) }
+
+        val content = extractContent(entry)
+
+        assertThat(content).isNotNull()
+        assertThat(content?.style).isEqualTo(Style.Base)
+    }
+
+    @Test
+    @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
     fun extractsContent_fromBigPictureStyle() {
         val entry = createEntry { setStyle(BigPictureStyle()) }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfoTest.java
index b2962ee..66277e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfoTest.java
@@ -198,7 +198,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         // and the feedback button is clicked,
         final View feedbackButton = mInfo.findViewById(R.id.notification_guts_bundle_feedback);
         feedbackButton.performClick();
@@ -253,7 +254,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         final View feedbackButton = mInfo.findViewById(R.id.notification_guts_bundle_feedback);
         feedbackButton.performClick();
@@ -294,7 +296,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         final View feedbackButton = mInfo.findViewById(R.id.notification_guts_bundle_feedback);
         feedbackButton.performClick();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 6a0a5bb..28b2ee8d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -269,6 +269,36 @@
     }
 
     @Test
+    fun testOpenAndCloseGutsWithoutSave() {
+        val guts = spy(NotificationGuts(mContext))
+        whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
+            handler.post(((invocation.arguments[0] as Runnable)))
+            null
+        }
+
+        // Test doesn't support animation since the guts view is not attached.
+        doNothing().whenever(guts).openControls(anyInt(), anyInt(), anyBoolean(), any())
+
+        val realRow = createTestNotificationRow()
+        val menuItem = createTestMenuItem(realRow)
+
+        val row = spy(realRow)
+        whenever(row.windowToken).thenReturn(Binder())
+        whenever(row.guts).thenReturn(guts)
+
+        assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
+        executor.runAllReady()
+        verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
+
+        gutsManager.closeAndUndoGuts()
+
+        verify(guts).closeControls(anyInt(), anyInt(), eq(false), eq(false))
+        verify(row, times(1)).setGutsView(any<MenuItem>())
+        executor.runAllReady()
+        verify(headsUpManager).setGutsShown(realRow.entry, false)
+    }
+
+    @Test
     fun testLockscreenShadeVisible_visible_gutsNotClosed() =
         testScope.runTest {
             // First, start out lockscreen or shade as not visible
@@ -377,52 +407,6 @@
         }
 
     @Test
-    fun testChangeDensityOrFontScale() {
-        val guts = spy(NotificationGuts(mContext))
-        whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
-            handler.post(((invocation.arguments[0] as Runnable)))
-            null
-        }
-
-        // Test doesn't support animation since the guts view is not attached.
-        doNothing().whenever(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
-
-        val realRow = createTestNotificationRow()
-        val menuItem = createTestMenuItem(realRow)
-
-        val row = spy(realRow)
-
-        whenever(row.windowToken).thenReturn(Binder())
-        whenever(row.guts).thenReturn(guts)
-        doNothing().whenever(row).ensureGutsInflated()
-
-        val realEntry = realRow.entry
-        val entry = spy(realEntry)
-
-        whenever(entry.row).thenReturn(row)
-        whenever(entry.guts).thenReturn(guts)
-
-        assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
-        executor.runAllReady()
-        verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
-
-        // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
-        verify(row).setGutsView(any<MenuItem>())
-
-        row.onDensityOrFontScaleChanged()
-        gutsManager.onDensityOrFontScaleChanged(entry)
-
-        executor.runAllReady()
-
-        gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
-
-        verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean())
-
-        // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
-        verify(row, times(2)).setGutsView(any<MenuItem>())
-    }
-
-    @Test
     fun testAppOpsSettingsIntent_camera() {
         val row = createTestNotificationRow()
         val ops = ArraySet<Int>()
@@ -544,6 +528,7 @@
                 /* wasShownHighPriority = */ eq(true),
                 eq(assistantFeedbackController),
                 eq(metricsLogger),
+                any<View.OnClickListener>(),
             )
     }
 
@@ -580,6 +565,7 @@
                 /* wasShownHighPriority = */ eq(false),
                 eq(assistantFeedbackController),
                 eq(metricsLogger),
+                any<View.OnClickListener>(),
             )
     }
 
@@ -614,6 +600,7 @@
                 /* wasShownHighPriority = */ eq(false),
                 eq(assistantFeedbackController),
                 eq(metricsLogger),
+                any<View.OnClickListener>(),
             )
     }
 
@@ -651,6 +638,7 @@
                 /* wasShownHighPriority = */ eq(false),
                 eq(assistantFeedbackController),
                 eq(metricsLogger),
+                any<View.OnClickListener>(),
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 245a6a0..fdba7ba 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -187,7 +187,7 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger, null);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
         assertTrue(textView.getText().toString().contains("App Name"));
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -213,7 +213,7 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger, null);
         final ImageView iconView = mNotificationInfo.findViewById(R.id.pkg_icon);
         assertEquals(iconDrawable, iconView.getDrawable());
     }
@@ -235,7 +235,7 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger, null);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(GONE, nameView.getVisibility());
     }
@@ -266,7 +266,7 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger, null);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(VISIBLE, nameView.getVisibility());
         assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -289,7 +289,7 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger, null);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(GONE, groupNameView.getVisibility());
     }
@@ -317,7 +317,7 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger, null);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(View.VISIBLE, groupNameView.getVisibility());
         assertEquals("Test Group Name", groupNameView.getText());
@@ -340,7 +340,7 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger, null);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
@@ -362,7 +362,7 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger, null);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, textView.getVisibility());
     }
@@ -388,7 +388,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -410,7 +411,8 @@
                 true,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -436,7 +438,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         settingsButton.performClick();
@@ -461,7 +464,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -486,7 +490,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -508,7 +513,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         mNotificationInfo.bindNotification(
                 mMockPackageManager,
                 mMockINotificationManager,
@@ -524,7 +530,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertEquals(View.VISIBLE, settingsButton.getVisibility());
     }
@@ -546,7 +553,8 @@
                 true,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -589,7 +597,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_call_text);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_call_desc),
@@ -632,7 +641,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         assertEquals(GONE,
                 mNotificationInfo.findViewById(R.id.non_configurable_call_text).getVisibility());
         assertEquals(VISIBLE,
@@ -659,7 +669,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
     }
@@ -681,7 +692,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
         assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
     }
@@ -705,7 +717,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         assertTrue(mNotificationInfo.findViewById(R.id.automatic).isSelected());
     }
 
@@ -726,7 +739,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         assertTrue(mNotificationInfo.findViewById(R.id.alert).isSelected());
     }
 
@@ -747,7 +761,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         assertTrue(mNotificationInfo.findViewById(R.id.silence).isSelected());
     }
 
@@ -768,7 +783,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
@@ -791,7 +807,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
         assertEquals(1, mUiEventLogger.numLogs());
         assertEquals(NotificationControlsEvent.NOTIFICATION_CONTROLS_OPEN.getId(),
                 mUiEventLogger.eventId(0));
@@ -815,7 +832,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mTestableLooper.processAllMessages();
@@ -842,7 +860,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mTestableLooper.processAllMessages();
@@ -869,7 +888,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.automatic).performClick();
         mTestableLooper.processAllMessages();
@@ -897,7 +917,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.handleCloseControls(true, false);
         mTestableLooper.processAllMessages();
@@ -924,7 +945,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.handleCloseControls(true, false);
         mTestableLooper.processAllMessages();
@@ -959,7 +981,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -987,7 +1010,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1028,7 +1052,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1065,7 +1090,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.automatic).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1097,7 +1123,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1133,7 +1160,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         assertEquals(mContext.getString(R.string.inline_done_button),
                 ((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1171,7 +1199,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.handleCloseControls(false, false);
@@ -1202,7 +1231,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         assertEquals(mContext.getString(R.string.inline_done_button),
                 ((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1240,7 +1270,8 @@
                 false,
                 true,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1269,7 +1300,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         assertEquals(mContext.getString(R.string.inline_done_button),
                 ((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1300,7 +1332,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1335,7 +1368,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1368,7 +1402,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1401,7 +1436,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
 
@@ -1427,7 +1463,8 @@
                 false,
                 false,
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                null);
 
         assertFalse(mNotificationInfo.willBeRemoved());
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index 6435e82..af67a04 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -16,29 +16,44 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
+import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
 import android.testing.TestableResources;
-import android.util.KeyValueListParser;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import androidx.test.annotation.UiThreadTest;
 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.animation.AnimatorTestRule;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.res.R;
 
+import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -46,8 +61,12 @@
 public class NotificationSnoozeTest extends SysuiTestCase {
     private static final int RES_DEFAULT = 2;
     private static final int[] RES_OPTIONS = {1, 2, 3};
-    private NotificationSnooze mNotificationSnooze;
-    private KeyValueListParser mMockParser;
+    private final NotificationSwipeActionHelper mSnoozeListener = mock(
+            NotificationSwipeActionHelper.class);
+    private NotificationSnooze mUnderTest;
+
+    @Rule
+    public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
 
     @Before
     public void setUp() throws Exception {
@@ -56,62 +75,117 @@
         TestableResources resources = mContext.getOrCreateTestableResources();
         resources.addOverride(R.integer.config_notification_snooze_time_default, RES_DEFAULT);
         resources.addOverride(R.array.config_notification_snooze_times, RES_OPTIONS);
-        mNotificationSnooze = new NotificationSnooze(mContext, null);
-        mMockParser = mock(KeyValueListParser.class);
+
+        mUnderTest = new NotificationSnooze(mContext, null);
+        mUnderTest.setSnoozeListener(mSnoozeListener);
+        mUnderTest.mExpandButton = mock(ImageView.class);
+        mUnderTest.mSnoozeView = mock(View.class);
+        mUnderTest.mSelectedOptionText = mock(TextView.class);
+        mUnderTest.mDivider = mock(View.class);
+        mUnderTest.mSnoozeOptionContainer = mock(ViewGroup.class);
+        mUnderTest.mSnoozeOptions = mock(List.class);
+    }
+
+    @After
+    public void tearDown() {
+        // Make sure all animations are finished
+        mAnimatorTestRule.advanceTimeBy(1000L);
     }
 
     @Test
-    public void testGetOptionsWithNoConfig() throws Exception {
-        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+    @EnableFlags(Flags.FLAG_NOTIFICATION_UNDO_GUTS_ON_CONFIG_CHANGED)
+    public void closeControls_withoutSave_performsUndo() {
+        ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
+        mUnderTest.mSelectedOption = options.getFirst();
+        mUnderTest.showSnoozeOptions(true);
+
+        assertThat(
+                mUnderTest.handleCloseControls(/* save = */ false, /* force = */ false)).isFalse();
+
+        assertThat(mUnderTest.mSelectedOption).isNull();
+        assertThat(mUnderTest.isExpanded()).isFalse();
+        verify(mSnoozeListener, times(0)).snooze(any(), any());
+    }
+
+    @Test
+    public void closeControls_whenExpanded_collapsesOptions() {
+        ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
+        mUnderTest.mSelectedOption = options.getFirst();
+        mUnderTest.showSnoozeOptions(true);
+
+        assertThat(mUnderTest.handleCloseControls(/* save = */ true, /* force = */ false)).isTrue();
+
+        assertThat(mUnderTest.mSelectedOption).isNotNull();
+        assertThat(mUnderTest.isExpanded()).isFalse();
+    }
+
+    @Test
+    public void closeControls_whenCollapsed_commitsChanges() {
+        ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
+        mUnderTest.mSelectedOption = options.getFirst();
+
+        assertThat(mUnderTest.handleCloseControls(/* save = */ true, /* force = */ false)).isTrue();
+
+        verify(mSnoozeListener).snooze(any(), any());
+    }
+
+    @Test
+    public void closeControls_withForce_returnsFalse() {
+        assertThat(mUnderTest.handleCloseControls(/* save = */ true, /* force = */ true)).isFalse();
+    }
+
+    @Test
+    public void testGetOptionsWithNoConfig() {
+        ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
         assertEquals(3, result.size());
         assertEquals(1, result.get(0).getMinutesToSnoozeFor());  // respect order
         assertEquals(2, result.get(1).getMinutesToSnoozeFor());
         assertEquals(3, result.get(2).getMinutesToSnoozeFor());
-        assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+        assertEquals(2, mUnderTest.getDefaultOption().getMinutesToSnoozeFor());
     }
 
     @Test
-    public void testGetOptionsWithInvalidConfig() throws Exception {
+    public void testGetOptionsWithInvalidConfig() {
         Settings.Global.putString(mContext.getContentResolver(),
                 Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
                 "this is garbage");
-        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+        ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
         assertEquals(3, result.size());
         assertEquals(1, result.get(0).getMinutesToSnoozeFor());  // respect order
         assertEquals(2, result.get(1).getMinutesToSnoozeFor());
         assertEquals(3, result.get(2).getMinutesToSnoozeFor());
-        assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+        assertEquals(2, mUnderTest.getDefaultOption().getMinutesToSnoozeFor());
     }
 
     @Test
-    public void testGetOptionsWithValidDefault() throws Exception {
+    public void testGetOptionsWithValidDefault() {
         Settings.Global.putString(mContext.getContentResolver(),
                 Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
                 "default=10,options_array=4:5:6:7");
-        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
-        assertNotNull(mNotificationSnooze.getDefaultOption());  // pick one
+        ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
+        assertNotNull(mUnderTest.getDefaultOption());  // pick one
     }
 
     @Test
-    public void testGetOptionsWithValidConfig() throws Exception {
+    public void testGetOptionsWithValidConfig() {
         Settings.Global.putString(mContext.getContentResolver(),
                 Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
                 "default=6,options_array=4:5:6:7");
-        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+        ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
         assertEquals(4, result.size());
         assertEquals(4, result.get(0).getMinutesToSnoozeFor());  // respect order
         assertEquals(5, result.get(1).getMinutesToSnoozeFor());
         assertEquals(6, result.get(2).getMinutesToSnoozeFor());
         assertEquals(7, result.get(3).getMinutesToSnoozeFor());
-        assertEquals(6, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+        assertEquals(6, mUnderTest.getDefaultOption().getMinutesToSnoozeFor());
     }
 
     @Test
-    public void testGetOptionsWithLongConfig() throws Exception {
+    public void testGetOptionsWithLongConfig() {
         Settings.Global.putString(mContext.getContentResolver(),
                 Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
                 "default=6,options_array=4:5:6:7:8:9:10:11:12:13:14:15:16:17");
-        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+        ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
         assertTrue(result.size() > 3);
         assertEquals(4, result.get(0).getMinutesToSnoozeFor());  // respect order
         assertEquals(5, result.get(1).getMinutesToSnoozeFor());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
new file mode 100644
index 0000000..b33f93d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.notification.row;
+
+import static android.app.Notification.EXTRA_BUILDER_APPLICATION_INFO;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.service.notification.StatusBarNotification;
+import android.telecom.TelecomManager;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.Dependency;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.AssistantFeedbackController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.concurrent.CountDownLatch;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper
+public class PromotedNotificationInfoTest extends SysuiTestCase {
+    private static final String TEST_PACKAGE_NAME = "test_package";
+    private static final int TEST_UID = 1;
+    private static final String TEST_CHANNEL = "test_channel";
+    private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
+
+    private TestableLooper mTestableLooper;
+    private PromotedNotificationInfo mInfo;
+    private NotificationChannel mNotificationChannel;
+    private StatusBarNotification mSbn;
+    private NotificationEntry mEntry;
+    private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
+
+    @Rule
+    public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock
+    private MetricsLogger mMetricsLogger;
+    @Mock
+    private INotificationManager mMockINotificationManager;
+    @Mock
+    private PackageManager mMockPackageManager;
+    @Mock
+    private OnUserInteractionCallback mOnUserInteractionCallback;
+    @Mock
+    private ChannelEditorDialogController mChannelEditorDialogController;
+    @Mock
+    private AssistantFeedbackController mAssistantFeedbackController;
+    @Mock
+    private TelecomManager mTelecomManager;
+
+    @Before
+    public void setUp() throws Exception {
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = TEST_UID;  // non-zero
+
+        mNotificationChannel = new NotificationChannel(
+                TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
+        Notification notification = new Notification();
+        notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, applicationInfo);
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+                notification, UserHandle.getUserHandleForUid(TEST_UID), null, 0);
+        mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
+        when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(false);
+
+        mTestableLooper = TestableLooper.get(this);
+
+        mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
+
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
+        // Inflate the layout
+        final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+        mInfo = (PromotedNotificationInfo) layoutInflater.inflate(
+                R.layout.promoted_notification_info, null);
+        mInfo.setGutsParent(mock(NotificationGuts.class));
+        // Our view is never attached to a window so the View#post methods in
+        // BundleNotificationInfo never get called. Setting this will skip the post and do the
+        // action immediately.
+        mInfo.mSkipPost = true;
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+    public void testBindNotification_setsOnClickListenerForFeedback() throws Exception {
+
+        // Bind the notification to the Info object
+        final CountDownLatch latch = new CountDownLatch(1);
+        mInfo.bindNotification(
+                mMockPackageManager,
+                mMockINotificationManager,
+                mOnUserInteractionCallback,
+                mChannelEditorDialogController,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mEntry,
+                null,
+                null,
+                mUiEventLogger,
+                true,
+                false,
+                true,
+                mAssistantFeedbackController,
+                mMetricsLogger,
+                null);
+        // Click demote button
+        final View demoteButton = mInfo.findViewById(R.id.promoted_demote);
+        demoteButton.performClick();
+        // verify that notiManager tried to demote
+        verify(mMockINotificationManager, atLeastOnce()).setCanBePromoted(TEST_PACKAGE_NAME,
+                mSbn.getUid(), false, true);
+
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
new file mode 100644
index 0000000..8163a68
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2025 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.notification.stack
+
+import android.os.testableLooper
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.haptics.msdl.fakeMSDLPlayer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.stack.MagneticNotificationRowManagerImpl.State
+import com.android.systemui.testKosmos
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
+
+    private val featureFlags = FakeFeatureFlagsClassic()
+    private val kosmos = testKosmos()
+    private val childrenNumber = 5
+    private val stackScrollLayout = mock<NotificationStackScrollLayout>()
+    private val sectionsManager = mock<NotificationSectionsManager>()
+    private val swipedMultiplier = 0.5f
+    private val msdlPlayer = kosmos.fakeMSDLPlayer
+
+    private val underTest = kosmos.magneticNotificationRowManagerImpl
+
+    private lateinit var notificationTestHelper: NotificationTestHelper
+    private lateinit var children: NotificationChildrenContainer
+
+    @Before
+    fun setUp() {
+        allowTestableLooperAsMainThread()
+        notificationTestHelper =
+            NotificationTestHelper(mContext, mDependency, kosmos.testableLooper, featureFlags)
+        children = notificationTestHelper.createGroup(childrenNumber).childrenContainer
+    }
+
+    @Test
+    fun setMagneticAndRoundableTargets_onIdle_targetsGetSet() =
+        kosmos.testScope.runTest {
+            // WHEN the targets are set for a row
+            val row = children.attachedChildren[childrenNumber / 2]
+            setTargetsForRow(row)
+
+            // THEN the magnetic and roundable targets are defined and the state is TARGETS_SET
+            assertThat(underTest.currentState).isEqualTo(State.TARGETS_SET)
+            assertThat(underTest.currentMagneticListeners.isNotEmpty()).isTrue()
+            assertThat(underTest.currentRoundableTargets).isNotNull()
+        }
+
+    @Test
+    fun setMagneticRowTranslation_whenTargetsAreSet_startsPulling() =
+        kosmos.testScope.runTest {
+            // GIVEN targets are set
+            val row = children.attachedChildren[childrenNumber / 2]
+            setTargetsForRow(row)
+
+            // WHEN setting a translation for the swiped row
+            underTest.setMagneticRowTranslation(row, translation = 100f)
+
+            // THEN the state moves to PULLING
+            assertThat(underTest.currentState).isEqualTo(State.PULLING)
+        }
+
+    @Test
+    fun setMagneticRowTranslation_whenRowIsNotSwiped_doesNotSetMagneticTranslation() =
+        kosmos.testScope.runTest {
+            // GIVEN that targets are set
+            val row = children.attachedChildren[childrenNumber / 2]
+            setTargetsForRow(row)
+
+            // WHEN setting a translation for a row that is not being swiped
+            val differentRow = children.attachedChildren[childrenNumber / 2 - 1]
+            val canSetMagneticTranslation =
+                underTest.setMagneticRowTranslation(differentRow, translation = 100f)
+
+            // THEN no magnetic translations are set
+            assertThat(canSetMagneticTranslation).isFalse()
+        }
+
+    @Test
+    fun setMagneticRowTranslation_belowThreshold_whilePulling_setsMagneticTranslations() =
+        kosmos.testScope.runTest {
+            // GIVEN a threshold of 100 px
+            val threshold = 100f
+            underTest.setSwipeThresholdPx(threshold)
+
+            // GIVEN that targets are set and the rows are being pulled
+            val row = children.attachedChildren[childrenNumber / 2]
+            setTargetsForRow(row)
+            underTest.setMagneticRowTranslation(row, translation = 100f)
+
+            // WHEN setting a translation that will fall below the threshold
+            val translation = threshold / swipedMultiplier - 50f
+            underTest.setMagneticRowTranslation(row, translation)
+
+            // THEN the targets continue to be pulled and translations are set
+            assertThat(underTest.currentState).isEqualTo(State.PULLING)
+            assertThat(row.translation).isEqualTo(swipedMultiplier * translation)
+        }
+
+    @Test
+    fun setMagneticRowTranslation_aboveThreshold_whilePulling_detachesMagneticTargets() =
+        kosmos.testScope.runTest {
+            // GIVEN a threshold of 100 px
+            val threshold = 100f
+            underTest.setSwipeThresholdPx(threshold)
+
+            // GIVEN that targets are set and the rows are being pulled
+            val row = children.attachedChildren[childrenNumber / 2]
+            setTargetsForRow(row)
+            underTest.setMagneticRowTranslation(row, translation = 100f)
+
+            // WHEN setting a translation that will fall above the threshold
+            val translation = threshold / swipedMultiplier + 50f
+            underTest.setMagneticRowTranslation(row, translation)
+
+            // THEN the swiped view detaches and the correct detach haptics play
+            assertThat(underTest.currentState).isEqualTo(State.DETACHED)
+            assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.SWIPE_THRESHOLD_INDICATOR)
+        }
+
+    @Test
+    fun setMagneticRowTranslation_whileDetached_setsTranslationAndStaysDetached() =
+        kosmos.testScope.runTest {
+            // GIVEN that the swiped view has been detached
+            val row = children.attachedChildren[childrenNumber / 2]
+            setDetachedState(row)
+
+            // WHEN setting a new translation
+            val translation = 300f
+            underTest.setMagneticRowTranslation(row, translation)
+
+            // THEN the swiped view continues to be detached
+            assertThat(underTest.currentState).isEqualTo(State.DETACHED)
+        }
+
+    @Test
+    fun onMagneticInteractionEnd_whilePulling_goesToIdle() =
+        kosmos.testScope.runTest {
+            // GIVEN targets are set
+            val row = children.attachedChildren[childrenNumber / 2]
+            setTargetsForRow(row)
+
+            // WHEN setting a translation for the swiped row
+            underTest.setMagneticRowTranslation(row, translation = 100f)
+
+            // WHEN the interaction ends on the row
+            underTest.onMagneticInteractionEnd(row, velocity = null)
+
+            // THEN the state resets
+            assertThat(underTest.currentState).isEqualTo(State.IDLE)
+        }
+
+    @Test
+    fun onMagneticInteractionEnd_whileDetached_goesToIdle() =
+        kosmos.testScope.runTest {
+            // GIVEN the swiped row is detached
+            val row = children.attachedChildren[childrenNumber / 2]
+            setDetachedState(row)
+
+            // WHEN the interaction ends on the row
+            underTest.onMagneticInteractionEnd(row, velocity = null)
+
+            // THEN the state resets
+            assertThat(underTest.currentState).isEqualTo(State.IDLE)
+        }
+
+    private fun setDetachedState(row: ExpandableNotificationRow) {
+        val threshold = 100f
+        underTest.setSwipeThresholdPx(threshold)
+
+        // Set the pulling state
+        setTargetsForRow(row)
+        underTest.setMagneticRowTranslation(row, translation = 100f)
+
+        // Set a translation that will fall above the threshold
+        val translation = threshold / swipedMultiplier + 50f
+        underTest.setMagneticRowTranslation(row, translation)
+
+        assertThat(underTest.currentState).isEqualTo(State.DETACHED)
+    }
+
+    private fun setTargetsForRow(row: ExpandableNotificationRow) {
+        underTest.setMagneticAndRoundableTargets(row, stackScrollLayout, sectionsManager)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 20cd6c7..4ce1380 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -165,6 +165,8 @@
     @Mock
     private SensitiveNotificationProtectionController mSensitiveNotificationProtectionController;
     @Mock private ExpandHelper mExpandHelper;
+    @Mock private MagneticNotificationRowManager mMagneticNotificationRowManager;
+    @Mock private NotificationSectionsManager mSectionsManager;
 
     @Captor
     private ArgumentCaptor<Runnable> mSensitiveStateListenerArgumentCaptor;
@@ -798,7 +800,9 @@
                 mActivityStarter,
                 new ResourcesSplitShadeStateController(),
                 mSensitiveNotificationProtectionController,
-                mWallpaperInteractor);
+                mWallpaperInteractor,
+                mMagneticNotificationRowManager,
+                mSectionsManager);
     }
 
     static class LogMatcher implements ArgumentMatcher<LogMaker> {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 50db9f7..4b8a0c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.stack
 
 import android.annotation.DimenRes
+import android.platform.test.annotations.EnableFlags
 import android.service.notification.StatusBarNotification
 import android.view.View.VISIBLE
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -30,6 +31,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
@@ -152,6 +154,29 @@
     }
 
     @Test
+    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    fun maxKeyguardNotificationsForPromotedOngoing_onLockscreenSpaceForMinHeightButNotIntrinsicHeight_returnsOne() {
+        setGapHeight(0f)
+        // No divider height since we're testing one element where index = 0
+
+        whenever(sysuiStatusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+        whenever(lockscreenShadeTransitionController.fractionToShade).thenReturn(0f)
+
+        val row = createMockRow(10f, isPromotedOngoing = true)
+        whenever(row.getMinHeight(any())).thenReturn(5)
+
+        val maxNotifications =
+            computeMaxKeyguardNotifications(
+                listOf(row),
+                /* spaceForNotifications= */ 5f,
+                /* spaceForShelf= */ 0f,
+                /* shelfHeight= */ 0f,
+            )
+
+        assertThat(maxNotifications).isEqualTo(1)
+    }
+
+    @Test
     fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() {
         setGapHeight(gapHeight)
         val shelfHeight = shelfHeight + dividerHeight
@@ -257,6 +282,26 @@
     }
 
     @Test
+    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    fun getSpaceNeeded_onLockscreenEnoughSpacePromotedOngoing_intrinsicHeight() {
+        setGapHeight(0f)
+        // No divider height since we're testing one element where index = 0
+
+        val row = createMockRow(10f, isPromotedOngoing = true)
+        whenever(row.getMinHeight(any())).thenReturn(5)
+
+        val space =
+            sizeCalculator.getSpaceNeeded(
+                row,
+                visibleIndex = 0,
+                previousView = null,
+                stack = stackLayout,
+                onLockscreen = true,
+            )
+        assertThat(space.whenEnoughSpace).isEqualTo(10f)
+    }
+
+    @Test
     fun getSpaceNeeded_onLockscreenEnoughSpaceNotStickyHun_minHeight() {
         setGapHeight(0f)
         // No divider height since we're testing one element where index = 0
@@ -296,6 +341,26 @@
     }
 
     @Test
+    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    fun getSpaceNeeded_onLockscreenSavingSpacePromotedOngoing_minHeight() {
+        setGapHeight(0f)
+        // No divider height since we're testing one element where index = 0
+
+        val expandableView = createMockRow(10f, isPromotedOngoing = true)
+        whenever(expandableView.getMinHeight(any())).thenReturn(5)
+
+        val space =
+            sizeCalculator.getSpaceNeeded(
+                expandableView,
+                visibleIndex = 0,
+                previousView = null,
+                stack = stackLayout,
+                onLockscreen = true,
+            )
+        assertThat(space.whenSavingSpace).isEqualTo(5)
+    }
+
+    @Test
     fun getSpaceNeeded_onLockscreenSavingSpaceNotStickyHun_minHeight() {
         setGapHeight(0f)
         // No divider height since we're testing one element where index = 0
@@ -366,6 +431,7 @@
         isSticky: Boolean = false,
         isRemoved: Boolean = false,
         visibility: Int = VISIBLE,
+        isPromotedOngoing: Boolean = false,
     ): ExpandableNotificationRow {
         val row = mock(ExpandableNotificationRow::class.java)
         val entry = mock(NotificationEntry::class.java)
@@ -378,6 +444,7 @@
         whenever(row.getMinHeight(any())).thenReturn(height.toInt())
         whenever(row.intrinsicHeight).thenReturn(height.toInt())
         whenever(row.heightWithoutLockscreenConstraints).thenReturn(height.toInt())
+        whenever(row.isPromotedOngoing).thenReturn(isPromotedOngoing)
         return row
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 789701f5..766ae73 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -405,7 +405,7 @@
         doNothing().when(mSwipeHelper).superSnapChild(mNotificationRow, 0, 0);
         mSwipeHelper.snapChild(mNotificationRow, 0, 0);
 
-        verify(mCallback, times(1)).onDragCancelled(mNotificationRow);
+        verify(mCallback, times(1)).onDragCancelledWithVelocity(mNotificationRow, 0);
         verify(mSwipeHelper, times(1)).superSnapChild(mNotificationRow, 0, 0);
         verify(mSwipeHelper, times(1)).handleMenuCoveredOrDismissed();
     }
@@ -416,7 +416,7 @@
         doNothing().when(mSwipeHelper).superSnapChild(mNotificationRow, 10, 0);
         mSwipeHelper.snapChild(mNotificationRow, 10, 0);
 
-        verify(mCallback, times(1)).onDragCancelled(mNotificationRow);
+        verify(mCallback, times(1)).onDragCancelledWithVelocity(mNotificationRow, 0);
         verify(mSwipeHelper, times(1)).superSnapChild(mNotificationRow, 10, 0);
         verify(mSwipeHelper, times(0)).handleMenuCoveredOrDismissed();
     }
@@ -426,7 +426,7 @@
         doNothing().when(mSwipeHelper).superSnapChild(mView, 10, 0);
         mSwipeHelper.snapChild(mView, 10, 0);
 
-        verify(mCallback).onDragCancelled(mView);
+        verify(mCallback).onDragCancelledWithVelocity(mView, 0);
         verify(mSwipeHelper, never()).superSnapChild(mView, 10, 0);
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index 87833d0..51de9d5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -6,8 +6,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
 import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
@@ -30,7 +32,7 @@
             NotificationTestHelper(mContext, mDependency, TestableLooper.get(this), featureFlags)
     }
 
-    private fun notificationTargetsHelper() = NotificationTargetsHelper(featureFlags)
+    private fun notificationTargetsHelper() = NotificationTargetsHelper()
 
     @Test
     fun targetsForFirstNotificationInGroup() {
@@ -93,4 +95,71 @@
             RoundableTargets(before = children.attachedChildren[1], swiped = swiped, after = null)
         assertEquals(expected, actual)
     }
+
+    @Test
+    fun findMagneticTargets_forMiddleChild_createsAllTargets() {
+        val childrenNumber = 5
+        val children = notificationTestHelper.createGroup(childrenNumber).childrenContainer
+
+        // WHEN the swiped view is the one at the middle of the container
+        val swiped = children.attachedChildren[childrenNumber / 2]
+
+        // THEN all the views that surround it become targets with the swiped view at the middle
+        val actual =
+            notificationTargetsHelper()
+                .findMagneticTargets(viewSwiped = swiped, stackScrollLayout = stackScrollLayout, 5)
+        assertMagneticTargetsForChildren(actual, children.attachedChildren)
+    }
+
+    @Test
+    fun findMagneticTargets_forTopChild_createsEligibleTargets() {
+        val childrenNumber = 5
+        val children = notificationTestHelper.createGroup(childrenNumber).childrenContainer
+
+        // WHEN the swiped view is the first one in the container
+        val swiped = children.attachedChildren[0]
+
+        // THEN the neighboring views become targets, with the swiped view at the middle and nulls
+        // to the left
+        val actual =
+            notificationTargetsHelper()
+                .findMagneticTargets(viewSwiped = swiped, stackScrollLayout = stackScrollLayout, 5)
+        val expectedRows =
+            listOf(null, null, swiped, children.attachedChildren[1], children.attachedChildren[2])
+        assertMagneticTargetsForChildren(actual, expectedRows)
+    }
+
+    @Test
+    fun findMagneticTargets_forBottomChild_createsEligibleTargets() {
+        val childrenNumber = 5
+        val children = notificationTestHelper.createGroup(childrenNumber).childrenContainer
+
+        // WHEN the view swiped is the last one in the container
+        val swiped = children.attachedChildren[childrenNumber - 1]
+
+        // THEN the neighboring views become targets, with the swiped view at the middle and nulls
+        // to the right
+        val actual =
+            notificationTargetsHelper()
+                .findMagneticTargets(viewSwiped = swiped, stackScrollLayout = stackScrollLayout, 5)
+        val expectedRows =
+            listOf(
+                children.attachedChildren[childrenNumber - 3],
+                children.attachedChildren[childrenNumber - 2],
+                swiped,
+                null,
+                null,
+            )
+        assertMagneticTargetsForChildren(actual, expectedRows)
+    }
+
+    private fun assertMagneticTargetsForChildren(
+        targets: List<MagneticRowListener?>,
+        children: List<ExpandableNotificationRow?>,
+    ) {
+        assertThat(targets.size).isEqualTo(children.size)
+        targets.forEachIndexed { i, target ->
+            assertThat(target).isEqualTo(children[i]?.magneticRowListener)
+        }
+    }
 }
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 a045b37..786b359 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
@@ -19,8 +19,6 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
@@ -64,9 +62,11 @@
 import com.android.systemui.scene.data.repository.Transition
 import com.android.systemui.scene.data.repository.setTransition
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
+import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.shade.mockLargeScreenHeaderHelper
 import com.android.systemui.shade.shadeTestUtil
-import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition
 import com.android.systemui.testKosmos
@@ -143,7 +143,7 @@
     @Test
     fun validateMarginStartInSplitShade() =
         testScope.runTest {
-            shadeTestUtil.setSplitShade(true)
+            kosmos.enableSplitShade()
             overrideDimensionPixelSize(R.dimen.notification_panel_margin_horizontal, 20)
 
             val dimens by collectLastValue(underTest.configurationBasedDimensions)
@@ -156,7 +156,7 @@
     @Test
     fun validateMarginStart() =
         testScope.runTest {
-            shadeTestUtil.setSplitShade(false)
+            kosmos.enableSingleShade()
             overrideDimensionPixelSize(R.dimen.notification_panel_margin_horizontal, 20)
 
             val dimens by collectLastValue(underTest.configurationBasedDimensions)
@@ -169,9 +169,9 @@
     @Test
     fun validateHorizontalPositionSingleShade() =
         testScope.runTest {
+            kosmos.enableSingleShade()
             overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
             val dimens by collectLastValue(underTest.configurationBasedDimensions)
-            shadeTestUtil.setSplitShade(false)
 
             val horizontalPosition = checkNotNull(dimens).horizontalPosition
             assertIs<HorizontalPosition.EdgeToEdge>(horizontalPosition)
@@ -180,9 +180,9 @@
     @Test
     fun validateHorizontalPositionSplitShade() =
         testScope.runTest {
+            kosmos.enableSplitShade()
             overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
             val dimens by collectLastValue(underTest.configurationBasedDimensions)
-            shadeTestUtil.setSplitShade(true)
 
             val horizontalPosition = checkNotNull(dimens).horizontalPosition
             assertIs<HorizontalPosition.MiddleToEdge>(horizontalPosition)
@@ -191,25 +191,22 @@
 
     @Test
     @EnableSceneContainer
-    @DisableFlags(DualShade.FLAG_NAME)
     fun validateHorizontalPositionInSceneContainerSingleShade() =
         testScope.runTest {
+            kosmos.enableSingleShade()
             overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
             val dimens by collectLastValue(underTest.configurationBasedDimensions)
-            shadeTestUtil.setSplitShade(false)
 
             val horizontalPosition = checkNotNull(dimens).horizontalPosition
             assertIs<HorizontalPosition.EdgeToEdge>(horizontalPosition)
         }
 
     @Test
-    @EnableSceneContainer
-    @DisableFlags(DualShade.FLAG_NAME)
     fun validateHorizontalPositionInSceneContainerSplitShade() =
         testScope.runTest {
+            kosmos.enableSplitShade()
             overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
             val dimens by collectLastValue(underTest.configurationBasedDimensions)
-            shadeTestUtil.setSplitShade(true)
 
             val horizontalPosition = checkNotNull(dimens).horizontalPosition
             assertIs<HorizontalPosition.MiddleToEdge>(horizontalPosition)
@@ -218,12 +215,11 @@
 
     @Test
     @EnableSceneContainer
-    @EnableFlags(DualShade.FLAG_NAME)
     fun validateHorizontalPositionInDualShade_narrowLayout() =
         testScope.runTest {
+            kosmos.enableDualShade(wideLayout = false)
             overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
             val dimens by collectLastValue(underTest.configurationBasedDimensions)
-            shadeTestUtil.setSplitShade(false)
 
             val horizontalPosition = checkNotNull(dimens).horizontalPosition
             assertIs<HorizontalPosition.EdgeToEdge>(horizontalPosition)
@@ -231,12 +227,11 @@
 
     @Test
     @EnableSceneContainer
-    @EnableFlags(DualShade.FLAG_NAME)
     fun validateHorizontalPositionInDualShade_wideLayout() =
         testScope.runTest {
+            kosmos.enableDualShade(wideLayout = true)
             overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
             val dimens by collectLastValue(underTest.configurationBasedDimensions)
-            shadeTestUtil.setSplitShade(true)
 
             val horizontalPosition = checkNotNull(dimens).horizontalPosition
             assertIs<HorizontalPosition.FloatAtStart>(horizontalPosition)
@@ -246,8 +241,8 @@
     @Test
     fun validatePaddingTopInSplitShade_usesLargeHeaderHelper() =
         testScope.runTest {
+            kosmos.enableSplitShade()
             whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
-            shadeTestUtil.setSplitShade(true)
             overrideResource(R.bool.config_use_large_screen_shade_header, true)
             overrideDimensionPixelSize(R.dimen.large_screen_shade_header_height, 10)
             overrideDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin, 50)
@@ -262,8 +257,8 @@
     @Test
     fun validatePaddingTopInNonSplitShade_usesLargeScreenHeader() =
         testScope.runTest {
+            kosmos.enableSingleShade()
             whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(10)
-            shadeTestUtil.setSplitShade(false)
             overrideResource(R.bool.config_use_large_screen_shade_header, true)
             overrideDimensionPixelSize(R.dimen.large_screen_shade_header_height, 10)
             overrideDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin, 50)
@@ -278,8 +273,8 @@
     @Test
     fun validatePaddingTopInNonSplitShade_doesNotUseLargeScreenHeader() =
         testScope.runTest {
+            kosmos.enableSingleShade()
             whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(10)
-            shadeTestUtil.setSplitShade(false)
             overrideResource(R.bool.config_use_large_screen_shade_header, false)
             overrideDimensionPixelSize(R.dimen.large_screen_shade_header_height, 10)
             overrideDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin, 50)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
index 43ad042..57b7df7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
@@ -162,7 +162,7 @@
 
         Mockito.`when`(iconManagerFactory.create(ArgumentMatchers.any(), ArgumentMatchers.any()))
             .thenReturn(iconManager)
-        Mockito.`when`(statusBarContentInsetsProviderStore.defaultDisplay)
+        Mockito.`when`(statusBarContentInsetsProviderStore.forDisplay(context.displayId))
             .thenReturn(kosmos.mockStatusBarContentInsetsProvider)
         allowTestableLooperAsMainThread()
         looper.runWithLooper {
@@ -180,6 +180,7 @@
     private fun createController(): KeyguardStatusBarViewController {
         return KeyguardStatusBarViewController(
             kosmos.testDispatcher,
+            context,
             keyguardStatusBarView,
             carrierTextController,
             configurationController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index 4e03933..4313aea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -36,7 +36,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.statusbar.StatusBarIcon
 import com.android.settingslib.notification.modes.TestModeBuilder
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
@@ -480,8 +479,6 @@
 
         override fun getZen(): Int = zen
 
-        override fun getManualRule(): ZenModeConfig.ZenRule = throw NotImplementedError()
-
         override fun getConfig(): ZenModeConfig = throw NotImplementedError()
 
         override fun getConsolidatedPolicy(): NotificationManager.Policy = consolidatedPolicy
@@ -490,14 +487,6 @@
 
         override fun isZenAvailable() = throw NotImplementedError()
 
-        override fun getEffectsSuppressor() = throw NotImplementedError()
-
-        override fun isCountdownConditionSupported() = throw NotImplementedError()
-
         override fun getCurrentUser() = throw NotImplementedError()
-
-        override fun isVolumeRestricted() = throw NotImplementedError()
-
-        override fun areNotificationsHiddenInShade() = throw NotImplementedError()
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index a1c910d..0223484 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -20,6 +20,7 @@
 import android.graphics.Rect
 import android.view.View
 import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
@@ -46,6 +47,9 @@
 
     override val statusBarPopupChips = MutableStateFlow(emptyList<PopupChipModel.Shown>())
 
+    override val mediaProjectionStopDialogDueToCallEndedState =
+        MutableStateFlow(MediaProjectionStopDialogModel.Hidden)
+
     override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)
 
     override val shouldHomeStatusBarBeVisible = MutableStateFlow(false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index e74d009..46f625f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -45,8 +45,8 @@
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.collectValues
 import com.android.systemui.kosmos.runTest
-import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.log.assertLogsWtf
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -58,7 +58,9 @@
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip
@@ -89,7 +91,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -98,9 +100,7 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
 class HomeStatusBarViewModelImplTest : SysuiTestCase() {
-    private val kosmos by lazy {
-        testKosmos().also { it.testDispatcher = UnconfinedTestDispatcher() }
-    }
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val Kosmos.underTest by Kosmos.Fixture { kosmos.homeStatusBarViewModel }
 
     @Before
@@ -112,6 +112,55 @@
     fun addDisplays() = runBlocking { kosmos.displayRepository.fake.addDisplay(DEFAULT_DISPLAY) }
 
     @Test
+    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun mediaProjectionStopDialogDueToCallEndedState_initiallyHidden() =
+        kosmos.runTest {
+            shareToAppChipViewModel.start()
+            val latest by collectLastValue(underTest.mediaProjectionStopDialogDueToCallEndedState)
+
+            // Verify that the stop dialog is initially hidden
+            assertThat(latest).isInstanceOf(MediaProjectionStopDialogModel.Hidden::class.java)
+        }
+
+    @Test
+    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun mediaProjectionStopDialogDueToCallEndedState_flagEnabled_mediaIsProjecting_projectionStartedDuringCallAndActivePostCallEventEmitted_isShown() =
+        kosmos.runTest {
+            shareToAppChipViewModel.start()
+
+            val latest by
+                collectLastValue(
+                    homeStatusBarViewModel.mediaProjectionStopDialogDueToCallEndedState
+                )
+
+            fakeMediaProjectionRepository.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            assertThat(latest).isInstanceOf(MediaProjectionStopDialogModel.Shown::class.java)
+        }
+
+    @Test
+    @DisableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+    fun mediaProjectionStopDialogDueToCallEndedState_flagDisabled_mediaIsProjecting_projectionStartedDuringCallAndActivePostCallEventEmitted_isHidden() =
+        kosmos.runTest {
+            shareToAppChipViewModel.start()
+
+            val latest by
+                collectLastValue(
+                    homeStatusBarViewModel.mediaProjectionStopDialogDueToCallEndedState
+                )
+
+            fakeMediaProjectionRepository.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()
+
+            assertThat(latest).isInstanceOf(MediaProjectionStopDialogModel.Hidden::class.java)
+        }
+
+    @Test
     fun isTransitioningFromLockscreenToOccluded_started_isTrue() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.isTransitioningFromLockscreenToOccluded)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
index abfd64a..2e43e52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -100,7 +100,9 @@
 
     @After
     fun tearDown() {
-        ViewUtils.detachView(view)
+        if (::view.isInitialized) {
+            ViewUtils.detachView(view)
+        }
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.kt
index 9abdf42..2225008 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.kt
@@ -91,45 +91,6 @@
     }
 
     @Test
-    fun testAreNotificationsHiddenInShade_zenOffShadeSuppressed() {
-        config.suppressedVisualEffects =
-            NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
-        controller.updateZenMode(Settings.Global.ZEN_MODE_OFF)
-        controller.updateZenModeConfig()
-        assertThat(controller.areNotificationsHiddenInShade()).isFalse()
-    }
-
-    @Test
-    fun testAreNotificationsHiddenInShade_zenOnShadeNotSuppressed() {
-        val policy =
-            NotificationManager.Policy(
-                0,
-                0,
-                0,
-                NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR,
-            )
-        whenever(mNm.consolidatedNotificationPolicy).thenReturn(policy)
-        controller.updateConsolidatedNotificationPolicy()
-        controller.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
-        assertThat(controller.areNotificationsHiddenInShade()).isFalse()
-    }
-
-    @Test
-    fun testAreNotificationsHiddenInShade_zenOnShadeSuppressed() {
-        val policy =
-            NotificationManager.Policy(
-                0,
-                0,
-                0,
-                NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST,
-            )
-        whenever(mNm.consolidatedNotificationPolicy).thenReturn(policy)
-        controller.updateConsolidatedNotificationPolicy()
-        controller.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
-        assertThat(controller.areNotificationsHiddenInShade()).isTrue()
-    }
-
-    @Test
     fun testModeChange() =
         testScope.runTest {
             val states =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 09be93d..ea91b7a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.content.res.Resources
 import android.hardware.devicestate.DeviceStateManager
+import android.os.PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.R
@@ -27,16 +28,20 @@
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
 import com.android.systemui.defaultDeviceState
 import com.android.systemui.deviceStateManager
-import com.android.systemui.display.data.repository.DeviceStateRepository
 import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.FOLDED
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.HALF_FOLDED
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.UNFOLDED
+import com.android.systemui.display.data.repository.fakeDeviceStateRepository
 import com.android.systemui.foldedDeviceStateList
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.power.shared.model.ScreenPowerState
-import com.android.systemui.power.shared.model.WakeSleepReason
-import com.android.systemui.power.shared.model.WakefulnessModel
-import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setScreenPowerState
+import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_OFF
+import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
 import com.android.systemui.shared.system.SysUiStatsLog
 import com.android.systemui.statusbar.policy.FakeConfigurationController
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
@@ -45,7 +50,7 @@
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
 import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
 import com.android.systemui.unfoldedDeviceState
-import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
+import com.android.systemui.util.animation.data.repository.fakeAnimationStatusRepository
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.time.FakeSystemClock
@@ -77,14 +82,15 @@
     private lateinit var displaySwitchLatencyTracker: DisplaySwitchLatencyTracker
     @Captor private lateinit var loggerArgumentCaptor: ArgumentCaptor<DisplaySwitchLatencyEvent>
 
+    private val kosmos = Kosmos()
     private val mockContext = mock<Context>()
     private val resources = mock<Resources>()
-    private val foldStateRepository = mock<DeviceStateRepository>()
-    private val powerInteractor = mock<PowerInteractor>()
-    private val animationStatusRepository = mock<AnimationStatusRepository>()
+    private val foldStateRepository = kosmos.fakeDeviceStateRepository
+    private val powerInteractor = PowerInteractorFactory.create().powerInteractor
+    private val animationStatusRepository = kosmos.fakeAnimationStatusRepository
     private val keyguardInteractor = mock<KeyguardInteractor>()
     private val displaySwitchLatencyLogger = mock<DisplaySwitchLatencyLogger>()
-    private val kosmos = Kosmos()
+
     private val deviceStateManager = kosmos.deviceStateManager
     private val closedDeviceState = kosmos.foldedDeviceStateList.first()
     private val openDeviceState = kosmos.unfoldedDeviceState
@@ -94,12 +100,7 @@
 
     private val testDispatcher: TestDispatcher = StandardTestDispatcher()
     private val testScope: TestScope = TestScope(testDispatcher)
-    private val isAsleep = MutableStateFlow(false)
     private val isAodAvailable = MutableStateFlow(false)
-    private val deviceState = MutableStateFlow(DeviceState.UNFOLDED)
-    private val screenPowerState = MutableStateFlow(ScreenPowerState.SCREEN_ON)
-    private val areAnimationEnabled = MutableStateFlow(true)
-    private val lastWakefulnessEvent = MutableStateFlow(WakefulnessModel())
     private val systemClock = FakeSystemClock()
     private val configurationController = FakeConfigurationController()
     private val configurationRepository =
@@ -126,13 +127,10 @@
             .thenReturn(listOf(closedDeviceState, openDeviceState))
         whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
             .thenReturn(nonEmptyClosedDeviceStatesArray)
-        whenever(foldStateRepository.state).thenReturn(deviceState)
-        whenever(powerInteractor.isAsleep).thenReturn(isAsleep)
-        whenever(animationStatusRepository.areAnimationsEnabled()).thenReturn(areAnimationEnabled)
-        whenever(powerInteractor.screenPowerState).thenReturn(screenPowerState)
         whenever(keyguardInteractor.isAodAvailable).thenReturn(isAodAvailable)
-        whenever(powerInteractor.detailedWakefulness).thenReturn(lastWakefulnessEvent)
-
+        animationStatusRepository.onAnimationStatusChanged(true)
+        powerInteractor.setAwakeForTest()
+        powerInteractor.setScreenPowerState(SCREEN_ON)
         displaySwitchLatencyTracker =
             DisplaySwitchLatencyTracker(
                 mockContext,
@@ -152,21 +150,19 @@
     @Test
     fun unfold_logsLatencyTillTransitionStarted() {
         testScope.runTest {
-            areAnimationEnabled.emit(true)
-
             displaySwitchLatencyTracker.start()
-            deviceState.emit(DeviceState.FOLDED)
-            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            setDeviceState(FOLDED)
+            powerInteractor.setScreenPowerState(SCREEN_OFF)
             systemClock.advanceTime(50)
             runCurrent()
-            deviceState.emit(DeviceState.HALF_FOLDED)
+            setDeviceState(HALF_FOLDED)
             runCurrent()
             systemClock.advanceTime(50)
-            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            powerInteractor.setScreenPowerState(SCREEN_ON)
             systemClock.advanceTime(200)
             unfoldTransitionProgressProvider.onTransitionStarted()
             runCurrent()
-            deviceState.emit(DeviceState.UNFOLDED)
+            setDeviceState(UNFOLDED)
 
             verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
             val loggedEvent = loggerArgumentCaptor.value
@@ -202,23 +198,22 @@
                     systemClock,
                     deviceStateManager,
                 )
-            areAnimationEnabled.emit(true)
 
             displaySwitchLatencyTracker.start()
-            deviceState.emit(DeviceState.FOLDED)
-            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            setDeviceState(FOLDED)
+            powerInteractor.setScreenPowerState(SCREEN_OFF)
             systemClock.advanceTime(50)
             runCurrent()
-            deviceState.emit(DeviceState.HALF_FOLDED)
+            setDeviceState(HALF_FOLDED)
             systemClock.advanceTime(50)
             runCurrent()
-            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            powerInteractor.setScreenPowerState(SCREEN_ON)
             systemClock.advanceTime(50)
             runCurrent()
             systemClock.advanceTime(200)
             unfoldTransitionProgressProvider.onTransitionStarted()
             runCurrent()
-            deviceState.emit(DeviceState.UNFOLDED)
+            setDeviceState(UNFOLDED)
 
             verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
             val loggedEvent = loggerArgumentCaptor.value
@@ -235,23 +230,23 @@
     @Test
     fun unfold_animationDisabled_logsLatencyTillScreenTurnedOn() {
         testScope.runTest {
-            areAnimationEnabled.emit(false)
+            animationStatusRepository.onAnimationStatusChanged(false)
 
             displaySwitchLatencyTracker.start()
-            deviceState.emit(DeviceState.FOLDED)
-            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            setDeviceState(FOLDED)
+            powerInteractor.setScreenPowerState(SCREEN_OFF)
             systemClock.advanceTime(50)
             runCurrent()
-            deviceState.emit(DeviceState.HALF_FOLDED)
+            setDeviceState(HALF_FOLDED)
             systemClock.advanceTime(50)
             runCurrent()
-            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            powerInteractor.setScreenPowerState(SCREEN_ON)
             systemClock.advanceTime(50)
             runCurrent()
             unfoldTransitionProgressProvider.onTransitionStarted()
             systemClock.advanceTime(200)
             runCurrent()
-            deviceState.emit(DeviceState.UNFOLDED)
+            setDeviceState(UNFOLDED)
 
             verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
             val loggedEvent = loggerArgumentCaptor.value
@@ -268,19 +263,18 @@
     @Test
     fun foldWhileStayingAwake_logsLatency() {
         testScope.runTest {
-            areAnimationEnabled.emit(true)
-            deviceState.emit(DeviceState.UNFOLDED)
-            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            setDeviceState(UNFOLDED)
+            powerInteractor.setScreenPowerState(SCREEN_ON)
 
             displaySwitchLatencyTracker.start()
-            deviceState.emit(DeviceState.HALF_FOLDED)
+            setDeviceState(HALF_FOLDED)
             systemClock.advanceTime(50)
             runCurrent()
-            deviceState.emit(DeviceState.FOLDED)
-            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            setDeviceState(FOLDED)
+            powerInteractor.setScreenPowerState(SCREEN_OFF)
             runCurrent()
             systemClock.advanceTime(200)
-            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            powerInteractor.setScreenPowerState(SCREEN_ON)
             runCurrent()
 
             verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
@@ -298,25 +292,19 @@
     @Test
     fun foldToAod_capturesToStateAsAod() {
         testScope.runTest {
-            areAnimationEnabled.emit(true)
-            deviceState.emit(DeviceState.UNFOLDED)
+            setDeviceState(UNFOLDED)
             isAodAvailable.emit(true)
 
             displaySwitchLatencyTracker.start()
-            deviceState.emit(DeviceState.HALF_FOLDED)
+            setDeviceState(HALF_FOLDED)
             systemClock.advanceTime(50)
             runCurrent()
-            deviceState.emit(DeviceState.FOLDED)
-            lastWakefulnessEvent.emit(
-                WakefulnessModel(
-                    internalWakefulnessState = WakefulnessState.ASLEEP,
-                    lastSleepReason = WakeSleepReason.FOLD,
-                )
-            )
-            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            setDeviceState(FOLDED)
+            powerInteractor.setAsleepForTest(sleepReason = GO_TO_SLEEP_REASON_DEVICE_FOLD)
+            powerInteractor.setScreenPowerState(SCREEN_OFF)
             runCurrent()
             systemClock.advanceTime(200)
-            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            powerInteractor.setScreenPowerState(SCREEN_ON)
             runCurrent()
 
             verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
@@ -335,22 +323,21 @@
     @Test
     fun fold_notAFoldable_shouldNotLogLatency() {
         testScope.runTest {
-            areAnimationEnabled.emit(true)
-            deviceState.emit(DeviceState.UNFOLDED)
+            setDeviceState(UNFOLDED)
             whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
                 .thenReturn(IntArray(0))
             whenever(deviceStateManager.supportedDeviceStates)
                 .thenReturn(listOf(defaultDeviceState))
 
             displaySwitchLatencyTracker.start()
-            deviceState.emit(DeviceState.HALF_FOLDED)
+            setDeviceState(HALF_FOLDED)
             systemClock.advanceTime(50)
             runCurrent()
-            deviceState.emit(DeviceState.FOLDED)
-            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            setDeviceState(FOLDED)
+            powerInteractor.setScreenPowerState(SCREEN_OFF)
             runCurrent()
             systemClock.advanceTime(200)
-            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            powerInteractor.setScreenPowerState(SCREEN_ON)
             runCurrent()
 
             verify(displaySwitchLatencyLogger, never()).log(any())
@@ -360,22 +347,16 @@
     @Test
     fun foldToScreenOff_capturesToStateAsScreenOff() {
         testScope.runTest {
-            areAnimationEnabled.emit(true)
-            deviceState.emit(DeviceState.UNFOLDED)
+            setDeviceState(UNFOLDED)
             isAodAvailable.emit(false)
 
             displaySwitchLatencyTracker.start()
-            deviceState.emit(DeviceState.HALF_FOLDED)
+            setDeviceState(HALF_FOLDED)
             systemClock.advanceTime(50)
             runCurrent()
-            deviceState.emit(DeviceState.FOLDED)
-            lastWakefulnessEvent.emit(
-                WakefulnessModel(
-                    internalWakefulnessState = WakefulnessState.ASLEEP,
-                    lastSleepReason = WakeSleepReason.FOLD,
-                )
-            )
-            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            setDeviceState(FOLDED)
+            powerInteractor.setAsleepForTest(sleepReason = GO_TO_SLEEP_REASON_DEVICE_FOLD)
+            powerInteractor.setScreenPowerState(SCREEN_OFF)
             runCurrent()
 
             verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
@@ -390,4 +371,8 @@
             assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
         }
     }
+
+    private suspend fun setDeviceState(state: DeviceState) {
+        foldStateRepository.emit(state)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt
index fec186e..b837253 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt
@@ -16,12 +16,16 @@
 
 package com.android.systemui.volume.dialog.domain.interactor
 
+import android.media.AudioManager.RINGER_MODE_NORMAL
+import android.media.AudioManager.RINGER_MODE_SILENT
+import android.media.AudioManager.RINGER_MODE_VIBRATE
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.plugins.fakeVolumeDialogController
 import com.android.systemui.testKosmos
 import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
 import com.google.common.truth.Truth.assertThat
@@ -44,4 +48,30 @@
             val event by collectLastValue(underTest.event)
             assertThat(event).isInstanceOf(VolumeDialogEventModel.SubscribedToEvents::class.java)
         }
+
+    @Test
+    fun showSilentHint_setsRingerModeToNormal() =
+        kosmos.runTest {
+            fakeVolumeDialogController.setRingerMode(RINGER_MODE_VIBRATE, false)
+
+            underTest // It should eagerly collect the values and update the controller
+            fakeVolumeDialogController.onShowSilentHint()
+            fakeVolumeDialogController.getState()
+
+            assertThat(fakeVolumeDialogController.state.ringerModeInternal)
+                .isEqualTo(RINGER_MODE_NORMAL)
+        }
+
+    @Test
+    fun showVibrateHint_setsRingerModeToSilent() =
+        kosmos.runTest {
+            fakeVolumeDialogController.setRingerMode(RINGER_MODE_VIBRATE, false)
+
+            underTest // It should eagerly collect the values and update the controller
+            fakeVolumeDialogController.onShowVibrateHint()
+            fakeVolumeDialogController.getState()
+
+            assertThat(fakeVolumeDialogController.state.ringerModeInternal)
+                .isEqualTo(RINGER_MODE_SILENT)
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt
index 7d55599..12885a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt
@@ -25,14 +25,13 @@
 import androidx.test.filters.SmallTest
 import com.android.settingslib.volume.shared.model.RingerMode
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.plugins.fakeVolumeDialogController
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -43,8 +42,7 @@
 @TestableLooper.RunWithLooper
 class VolumeDialogRingerInteractorTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val controller = kosmos.fakeVolumeDialogController
 
     private lateinit var underTest: VolumeDialogRingerInteractor
@@ -57,13 +55,11 @@
 
     @Test
     fun setRingerMode_normal() =
-        testScope.runTest {
-            runCurrent()
+        kosmos.runTest {
             val ringerModel by collectLastValue(underTest.ringerModel)
 
             underTest.setRingerMode(RingerMode(RINGER_MODE_NORMAL))
             controller.getState()
-            runCurrent()
 
             assertThat(ringerModel).isNotNull()
             assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_NORMAL))
@@ -71,13 +67,11 @@
 
     @Test
     fun setRingerMode_silent() =
-        testScope.runTest {
-            runCurrent()
+        kosmos.runTest {
             val ringerModel by collectLastValue(underTest.ringerModel)
 
             underTest.setRingerMode(RingerMode(RINGER_MODE_SILENT))
             controller.getState()
-            runCurrent()
 
             assertThat(ringerModel).isNotNull()
             assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_SILENT))
@@ -85,13 +79,11 @@
 
     @Test
     fun setRingerMode_vibrate() =
-        testScope.runTest {
-            runCurrent()
+        kosmos.runTest {
             val ringerModel by collectLastValue(underTest.ringerModel)
 
             underTest.setRingerMode(RingerMode(RINGER_MODE_VIBRATE))
             controller.getState()
-            runCurrent()
 
             assertThat(ringerModel).isNotNull()
             assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_VIBRATE))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt
index 799ca4a..0a50722 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager
 import android.testing.TestableLooper
-import android.view.MotionEvent
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -29,6 +28,7 @@
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.dialog.sliders.shared.model.SliderInputEvent
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlin.time.Duration.Companion.seconds
@@ -73,16 +73,7 @@
                 assertThat(dialogVisibility)
                     .isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
 
-                underTest.onTouchEvent(
-                    MotionEvent.obtain(
-                        /* downTime = */ 0,
-                        /* eventTime = */ 0,
-                        /* action = */ 0,
-                        /* x = */ 0f,
-                        /* y = */ 0f,
-                        /* metaState = */ 0,
-                    )
-                )
+                underTest.onTouchEvent(SliderInputEvent.Touch.Start(0f, 0f))
                 advanceTimeBy(volumeDialogTimeout / 2)
 
                 assertThat(dialogVisibility)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
index ba6ea9f..b4fbaad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
@@ -16,11 +16,15 @@
 
 package com.android.systemui.wallpapers
 
+import android.app.Flags
 import android.content.Context
+import android.content.res.Resources
 import android.graphics.Canvas
 import android.graphics.Paint
 import android.graphics.Rect
 import android.graphics.RectF
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.service.wallpaper.WallpaperService.Engine
 import android.testing.TestableLooper.RunWithLooper
 import android.view.Surface
@@ -28,15 +32,20 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyFloat
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.spy
 import org.mockito.MockitoAnnotations
 import org.mockito.kotlin.any
+import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
 import org.mockito.kotlin.whenever
 
 @SmallTest
@@ -52,6 +61,8 @@
 
     @Mock private lateinit var mockContext: Context
 
+    @Mock private lateinit var mockResources: Resources
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -60,6 +71,13 @@
         whenever(surfaceHolder.surfaceFrame).thenReturn(surfaceFrame)
         whenever(surface.lockHardwareCanvas()).thenReturn(canvas)
         whenever(mockContext.getColor(anyInt())).thenReturn(1)
+        whenever(mockContext.resources).thenReturn(mockResources)
+        whenever(
+                mockResources.getDimensionPixelOffset(
+                    eq(R.dimen.gradient_color_wallpaper_center_offset)
+                )
+            )
+            .thenReturn(OFFSET_PX)
     }
 
     private fun createGradientColorWallpaperEngine(): Engine {
@@ -70,6 +88,18 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+    fun onSurfaceRedrawNeeded_flagDisabled_shouldNotDrawInCanvas() {
+        val engine = createGradientColorWallpaperEngine()
+        engine.onCreate(surfaceHolder)
+
+        engine.onSurfaceRedrawNeeded(surfaceHolder)
+
+        verifyZeroInteractions(canvas)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
     fun onSurfaceRedrawNeeded_shouldDrawInCanvas() {
         val engine = createGradientColorWallpaperEngine()
         engine.onCreate(surfaceHolder)
@@ -77,9 +107,11 @@
         engine.onSurfaceRedrawNeeded(surfaceHolder)
 
         verify(canvas).drawRect(any<RectF>(), any<Paint>())
+        verify(canvas, times(2)).drawCircle(anyFloat(), anyFloat(), anyFloat(), any<Paint>())
     }
 
     private companion object {
         val surfaceFrame = Rect(0, 0, 100, 100)
+        const val OFFSET_PX = 100
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
index 2985053..d6343c8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -26,4 +26,5 @@
     override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null)
     override val wallpaperSupportsAmbientMode = flowOf(false)
     override var rootView: View? = null
+    override val shouldSendFocalArea = MutableStateFlow(false)
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
index 03753d9..115edd0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
@@ -67,7 +67,6 @@
             fakeBroadcastDispatcher,
             userRepository,
             keyguardRepository,
-            keyguardClockRepository,
             wallpaperManager,
             context,
         )
@@ -252,7 +251,7 @@
     @EnableFlags(Flags.FLAG_MAGIC_PORTRAIT_WALLPAPERS)
     fun shouldSendNotificationLayout_setMagicPortraitWallpaper_launchSendLayoutJob() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.shouldSendNotificationLayout)
+            val latest by collectLastValue(underTest.shouldSendFocalArea)
             val magicPortraitWallpaper =
                 mock<WallpaperInfo>().apply {
                     whenever(this.component)
@@ -273,7 +272,7 @@
     @EnableFlags(Flags.FLAG_MAGIC_PORTRAIT_WALLPAPERS)
     fun shouldSendNotificationLayout_setNotMagicPortraitWallpaper_cancelSendLayoutJob() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.shouldSendNotificationLayout)
+            val latest by collectLastValue(underTest.shouldSendFocalArea)
             val magicPortraitWallpaper = MAGIC_PORTRAIT_WP
             whenever(wallpaperManager.getWallpaperInfoForUser(any()))
                 .thenReturn(magicPortraitWallpaper)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
new file mode 100644
index 0000000..6a9bbca1
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2025 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.plugins.clocks
+
+import android.view.View
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.core.MessageBuffer
+import kotlin.math.abs
+
+class ClockLogger(private val view: View?, buffer: MessageBuffer, tag: String) :
+    Logger(buffer, tag) {
+
+    private var loggedAlpha = 1000f
+    private val isDrawn: Boolean
+        get() = ((view?.mPrivateFlags ?: 0x0) and 0x20 /* PFLAG_DRAWN */) > 0
+
+    fun invalidate() {
+        if (isDrawn && view?.visibility == View.VISIBLE) {
+            d("invalidate()")
+        }
+    }
+
+    fun refreshTime() {
+        d("refreshTime()")
+    }
+
+    fun requestLayout() {
+        if (view?.isLayoutRequested() == false) {
+            d("requestLayout()")
+        }
+    }
+
+    fun onMeasure() {
+        d("onMeasure()")
+    }
+
+    fun onLayout() {
+        d("onLayout()")
+    }
+
+    fun onDraw() {
+        d("onDraw()")
+    }
+
+    fun onDraw(str: String?) {
+        d({ "onDraw(${escapeTime(str1)})" }) { str1 = str ?: "" }
+    }
+
+    fun onDraw(lsStr: String?, aodStr: String?) {
+        d({ "onDraw(ls = ${escapeTime(str1)}, aod = ${escapeTime(str2)}" }) {
+            str1 = lsStr
+            str2 = aodStr
+        }
+    }
+
+    fun setVisibility(visibility: Int) {
+        if (visibility != view?.visibility) {
+            d({ "setVisibility(${getVisText(int1)})" }) { int1 = visibility }
+        }
+    }
+
+    fun setAlpha(alpha: Float) {
+        val delta = if (alpha <= 0f || alpha >= 1f) 0.001f else 0.5f
+        if (abs(loggedAlpha - alpha) >= delta) {
+            loggedAlpha = alpha
+            d({ "setAlpha($double1)" }) { double1 = alpha.toDouble() }
+        }
+    }
+
+    fun addView(child: View) {
+        d({ "addView($str1 @$int1)" }) {
+            str1 = child::class.simpleName!!
+            int1 = child.id
+        }
+    }
+
+    companion object {
+        // Used when MessageBuffers are not provided by the host application
+        val DEFAULT_MESSAGE_BUFFER = LogcatOnlyMessageBuffer(LogLevel.INFO)
+
+        // Debug is primarially used for tests, but can also be used for tracking down hard issues.
+        val DEBUG_MESSAGE_BUFFER = LogcatOnlyMessageBuffer(LogLevel.DEBUG)
+
+        // Only intended for use during initialization steps before the logger is initialized
+        val INIT_LOGGER = ClockLogger(null, LogcatOnlyMessageBuffer(LogLevel.ERROR), "CLOCK_INIT")
+
+        @JvmStatic
+        fun getVisText(visibility: Int): String {
+            return when (visibility) {
+                View.GONE -> "GONE"
+                View.INVISIBLE -> "INVISIBLE"
+                View.VISIBLE -> "VISIBLE"
+                else -> "$visibility"
+            }
+        }
+
+        @JvmStatic
+        fun escapeTime(timeStr: String?): String? {
+            return timeStr?.replace("\n", "\\n")
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/TableLogBufferBase.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/TableLogBufferBase.kt
index 50b3f78..8a962f9 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/TableLogBufferBase.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/TableLogBufferBase.kt
@@ -25,34 +25,34 @@
      *
      * For Java overloading.
      */
-    fun logChange(prefix: String, columnName: String, value: String?) {
+    fun logChange(prefix: String = "", columnName: String, value: String?) {
         logChange(prefix, columnName, value, isInitial = false)
     }
 
     /** Logs a String? change. */
-    fun logChange(prefix: String, columnName: String, value: String?, isInitial: Boolean)
+    fun logChange(prefix: String = "", columnName: String, value: String?, isInitial: Boolean)
 
     /**
      * Logs a Boolean change.
      *
      * For Java overloading.
      */
-    fun logChange(prefix: String, columnName: String, value: Boolean) {
+    fun logChange(prefix: String = "", columnName: String, value: Boolean) {
         logChange(prefix, columnName, value, isInitial = false)
     }
 
     /** Logs a Boolean change. */
-    fun logChange(prefix: String, columnName: String, value: Boolean, isInitial: Boolean)
+    fun logChange(prefix: String = "", columnName: String, value: Boolean, isInitial: Boolean)
 
     /**
      * Logs an Int? change.
      *
      * For Java overloading.
      */
-    fun logChange(prefix: String, columnName: String, value: Int?) {
+    fun logChange(prefix: String = "", columnName: String, value: Int?) {
         logChange(prefix, columnName, value, isInitial = false)
     }
 
     /** Logs an Int? change. */
-    fun logChange(prefix: String, columnName: String, value: Int?, isInitial: Boolean)
+    fun logChange(prefix: String = "", columnName: String, value: Int?, isInitial: Boolean)
 }
diff --git a/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml b/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
index f7dac13..ea494b4 100644
--- a/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
@@ -21,7 +21,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/keyguard_lock_padding"
-        android:focusable="true"
+        android:focusable="false"
          />
 
     <com.android.keyguard.BouncerKeyguardMessageArea
@@ -30,6 +30,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/secondary_message_padding"
-        android:focusable="true" />
+        android:focusable="false" />
 
 </merge>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
index 2a8f1b5..f231df2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
@@ -66,7 +66,7 @@
 
             <com.android.systemui.bouncer.ui.BouncerMessageView
                 android:id="@+id/bouncer_message_view"
-                android:importantForAccessibility="noHideDescendants"
+                android:screenReaderFocusable="true"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 76f6f59..0445722 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -31,7 +31,7 @@
 
     <com.android.systemui.bouncer.ui.BouncerMessageView
         android:id="@+id/bouncer_message_view"
-        android:importantForAccessibility="noHideDescendants"
+        android:screenReaderFocusable="true"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
index 5879c11..b184344 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
@@ -67,7 +67,7 @@
 
             <com.android.systemui.bouncer.ui.BouncerMessageView
                 android:id="@+id/bouncer_message_view"
-                android:importantForAccessibility="noHideDescendants"
+                android:screenReaderFocusable="true"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index 3f7b028..0e15ff66 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -35,7 +35,7 @@
 
     <com.android.systemui.bouncer.ui.BouncerMessageView
         android:id="@+id/bouncer_message_view"
-        android:importantForAccessibility="noHideDescendants"
+        android:screenReaderFocusable="true"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
index b464fb3..f6ac02a 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
@@ -74,7 +74,7 @@
 
             <com.android.systemui.bouncer.ui.BouncerMessageView
                 android:id="@+id/bouncer_message_view"
-                android:importantForAccessibility="noHideDescendants"
+                android:screenReaderFocusable="true"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 2158073..ba4da79 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -32,7 +32,7 @@
 
     <com.android.systemui.bouncer.ui.BouncerMessageView
         android:id="@+id/bouncer_message_view"
-        android:importantForAccessibility="noHideDescendants"
+        android:screenReaderFocusable="true"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
index cc99f5e..dd5f7e4 100644
--- a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
+++ b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
@@ -30,7 +30,7 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_weight="1"
-        android:textAppearance="@style/TextAppearance.QS.Status.Carriers"
+        android:textAppearance="@style/TextAppearance.QS.Status"
         android:layout_marginEnd="@dimen/qs_carrier_margin_width"
         android:visibility="gone"
         android:textDirection="locale"
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 433c0a7..e7d6b2f 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -159,6 +159,17 @@
         <item name="android:shadowRadius">?attr/shadowRadius</item>
     </style>
 
+    <style name="TextAppearance.Keyguard.BottomArea.DoubleShadow">
+        <item name="keyShadowBlur">0.5dp</item>
+        <item name="keyShadowOffsetX">0.5dp</item>
+        <item name="keyShadowOffsetY">0.5dp</item>
+        <item name="keyShadowAlpha">0.8</item>
+        <item name="ambientShadowBlur">0.5dp</item>
+        <item name="ambientShadowOffsetX">0.5dp</item>
+        <item name="ambientShadowOffsetY">0.5dp</item>
+        <item name="ambientShadowAlpha">0.6</item>
+    </style>
+
     <style name="TextAppearance.Keyguard.BottomArea.Button">
         <item name="android:shadowRadius">0</item>
     </style>
diff --git a/packages/SystemUI/res/drawable/media_output_item_expand_group.xml b/packages/SystemUI/res/drawable/media_output_item_expand_group.xml
new file mode 100644
index 0000000..833843d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_item_expand_group.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,15.4 L6,9.4l1.4,-1.4 4.6,4.6 4.6,-4.6 1.4,1.4 -6,6Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
index 1d5e09d..e1e6092 100644
--- a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
@@ -26,6 +26,9 @@
                 <padding
                     android:left="20dp"
                     android:right="20dp" />
+                <!-- TODO(b/294830092): Update to the blur surface effect color token when
+                                        the resource workaround is resolved and
+                                        notification_shade_blur is enabled. -->
                 <solid android:color="@androidprv:color/materialColorSurfaceContainerHigh" />
             </shape>
         </inset>
diff --git a/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
index e47fc62..8944efb 100644
--- a/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
+++ b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
@@ -42,7 +42,6 @@
         android:layout_height="wrap_content"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="@androidprv:color/materialColorOnSecondaryFixed"
-        android:textSize="14sp"
         android:maxLines="1"
         android:ellipsize="end" />
 
diff --git a/packages/SystemUI/res/layout/low_light_clock_dream.xml b/packages/SystemUI/res/layout/low_light_clock_dream.xml
new file mode 100644
index 0000000..3d74a9f
--- /dev/null
+++ b/packages/SystemUI/res/layout/low_light_clock_dream.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/low_light_clock_dream"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/low_light_clock_background_color">
+
+    <TextClock
+        android:id="@+id/low_light_text_clock"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/low_light_clock_text_size"
+        android:layout_gravity="center"
+        android:fontFamily="google-sans-clock"
+        android:gravity="center_horizontal"
+        android:textColor="@color/low_light_clock_text_color"
+        android:autoSizeTextType="uniform"
+        android:autoSizeMaxTextSize="@dimen/low_light_clock_text_size"
+        android:format12Hour="h:mm"
+        android:format24Hour="H:mm"/>
+
+    <TextView
+        android:id="@+id/charging_status_text_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
+        android:gravity="center"
+        android:minHeight="@dimen/low_light_clock_charging_text_min_height"
+        android:layout_gravity="center_horizontal|bottom"
+        android:paddingStart="@dimen/keyguard_indication_text_padding"
+        android:paddingEnd="@dimen/keyguard_indication_text_padding"
+        android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+        android:textSize="@dimen/low_light_clock_charging_text_size"
+        android:textFontWeight="@integer/low_light_clock_charging_text_font_weight"
+        android:maxLines="2"
+        android:ellipsize="end"
+        android:accessibilityLiveRegion="polite" />
+  </FrameLayout>
+
diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
index 21e0d2c..69117cf 100644
--- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
@@ -52,13 +52,13 @@
             android:layout_height="64dp"
             android:focusable="false"
             android:importantForAccessibility="no"
-            android:background="@drawable/media_output_title_icon_area"
             android:layout_gravity="center_vertical|start">
             <ImageView
                 android:id="@+id/title_icon"
                 android:layout_width="24dp"
                 android:layout_height="24dp"
                 android:focusable="false"
+                android:contentDescription="@null"
                 android:importantForAccessibility="no"
                 android:animateLayoutChanges="true"
                 android:layout_gravity="center"/>
@@ -75,30 +75,17 @@
                 android:visibility="gone"/>
         </FrameLayout>
 
-        <TextView
-            android:id="@+id/title"
-            android:focusable="false"
-            android:importantForAccessibility="no"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical|start"
-            android:layout_marginStart="72dp"
-            android:layout_marginEnd="56dp"
-            android:ellipsize="end"
-            android:maxLines="1"
-            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-            android:textSize="16sp"/>
-
         <LinearLayout
-            android:id="@+id/two_line_layout"
             android:orientation="vertical"
             android:layout_width="wrap_content"
             android:layout_gravity="center_vertical|start"
+            android:gravity="center_vertical|start"
             android:layout_height="48dp"
             android:layout_marginEnd="56dp"
             android:layout_marginStart="72dp">
             <TextView
-                android:id="@+id/two_line_title"
+                android:id="@+id/title"
+                android:importantForAccessibility="no"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:ellipsize="end"
diff --git a/packages/SystemUI/res/layout/notification_2025_hybrid.xml b/packages/SystemUI/res/layout/notification_2025_hybrid.xml
index 8c34cd4..8fd10fb 100644
--- a/packages/SystemUI/res/layout/notification_2025_hybrid.xml
+++ b/packages/SystemUI/res/layout/notification_2025_hybrid.xml
@@ -29,6 +29,7 @@
         android:layout_height="wrap_content"
         android:singleLine="true"
         android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+        android:textSize="@*android:dimen/notification_2025_title_text_size"
         android:paddingEnd="4dp"
     />
     <TextView
diff --git a/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml b/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml
index a338e4c..35f2ef9 100644
--- a/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml
+++ b/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml
@@ -54,6 +54,7 @@
         android:singleLine="true"
         android:paddingEnd="4dp"
         android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+        android:textSize="@*android:dimen/notification_2025_title_text_size"
     />
 
     <TextView
diff --git a/packages/SystemUI/res/layout/privacy_dialog_item_v2.xml b/packages/SystemUI/res/layout/privacy_dialog_item_v2.xml
index 3ca4b94..aefe927 100644
--- a/packages/SystemUI/res/layout/privacy_dialog_item_v2.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog_item_v2.xml
@@ -77,6 +77,7 @@
         </LinearLayout>
         <LinearLayout
             android:id="@+id/privacy_dialog_item_header_expanded_layout"
+            android:importantForAccessibility="no"
             android:orientation="vertical"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/promoted_notification_info.xml b/packages/SystemUI/res/layout/promoted_notification_info.xml
new file mode 100644
index 0000000..5d170a9
--- /dev/null
+++ b/packages/SystemUI/res/layout/promoted_notification_info.xml
@@ -0,0 +1,387 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright 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.
+-->
+
+<com.android.systemui.statusbar.notification.row.PromotedNotificationInfo
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/notification_guts"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:focusable="true"
+    android:clipChildren="false"
+    android:clipToPadding="true"
+    android:orientation="vertical"
+    android:paddingStart="@dimen/notification_shade_content_margin_horizontal">
+
+    <!-- Package Info -->
+    <LinearLayout
+        android:id="@+id/header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:clipChildren="false"
+        android:paddingTop="@dimen/notification_guts_header_top_padding"
+        android:clipToPadding="true">
+        <ImageView
+            android:id="@+id/pkg_icon"
+            android:layout_width="@dimen/notification_guts_conversation_icon_size"
+            android:layout_height="@dimen/notification_guts_conversation_icon_size"
+            android:layout_centerVertical="true"
+            android:layout_alignParentStart="true"
+            android:layout_marginEnd="15dp" />
+        <LinearLayout
+            android:id="@+id/names"
+            android:layout_weight="1"
+            android:layout_width="0dp"
+            android:orientation="vertical"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/notification_guts_conversation_icon_size"
+            android:layout_centerVertical="true"
+            android:gravity="center_vertical"
+            android:layout_alignEnd="@id/pkg_icon"
+            android:layout_toEndOf="@id/pkg_icon">
+            <TextView
+                android:id="@+id/channel_name"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textDirection="locale"
+                style="@style/TextAppearance.NotificationImportanceChannel"/>
+            <TextView
+                android:id="@+id/group_name"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textDirection="locale"
+                android:ellipsize="end"
+                style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
+            <TextView
+                android:id="@+id/pkg_name"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                style="@style/TextAppearance.NotificationImportanceApp"
+                android:ellipsize="end"
+                android:textDirection="locale"
+                android:maxLines="1"/>
+            <TextView
+                android:id="@+id/delegate_name"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                style="@style/TextAppearance.NotificationImportanceHeader"
+                android:layout_marginStart="2dp"
+                android:layout_marginEnd="2dp"
+                android:ellipsize="end"
+                android:textDirection="locale"
+                android:text="@string/notification_delegate_header"
+                android:maxLines="1" />
+
+        </LinearLayout>
+
+        <!-- end aligned fields -->
+        <!-- Optional link to app. Only appears if the channel is not disabled and the app
+asked for it -->
+        <ImageButton
+            android:id="@+id/app_settings"
+            android:layout_width="@dimen/notification_importance_toggle_size"
+            android:layout_height="@dimen/notification_importance_toggle_size"
+            android:layout_centerVertical="true"
+            android:visibility="gone"
+            android:background="@drawable/ripple_drawable"
+            android:contentDescription="@string/notification_app_settings"
+            android:src="@drawable/ic_info"
+            android:layout_toStartOf="@id/info"
+            android:tint="@androidprv:color/materialColorPrimary"/>
+        <ImageButton
+            android:id="@+id/info"
+            android:layout_width="@dimen/notification_importance_toggle_size"
+            android:layout_height="@dimen/notification_importance_toggle_size"
+            android:layout_centerVertical="true"
+            android:contentDescription="@string/notification_more_settings"
+            android:background="@drawable/ripple_drawable_20dp"
+            android:src="@drawable/ic_settings"
+            android:tint="@androidprv:color/materialColorPrimary"
+            android:layout_alignParentEnd="true" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/inline_controls"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingEnd="@dimen/notification_shade_content_margin_horizontal"
+        android:layout_marginTop="@dimen/notification_guts_option_vertical_padding"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:orientation="vertical">
+
+        <!-- Non configurable app/channel text. appears instead of @+id/interruptiveness_settings-->
+        <TextView
+            android:id="@+id/non_configurable_text"
+            android:text="@string/notification_unblockable_desc"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="@*android:style/TextAppearance.DeviceDefault.Notification" />
+
+        <!-- Non configurable app/channel text. appears instead of @+id/interruptiveness_settings-->
+        <TextView
+            android:id="@+id/non_configurable_call_text"
+            android:text="@string/notification_unblockable_call_desc"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="@*android:style/TextAppearance.DeviceDefault.Notification" />
+
+        <!-- Non configurable multichannel text. appears instead of @+id/interruptiveness_settings-->
+        <TextView
+            android:id="@+id/non_configurable_multichannel_text"
+            android:text="@string/notification_multichannel_desc"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="@*android:style/TextAppearance.DeviceDefault.Notification" />
+
+        <LinearLayout
+            android:id="@+id/interruptiveness_settings"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:orientation="vertical">
+            <com.android.systemui.statusbar.notification.row.ButtonLinearLayout
+                android:id="@+id/automatic"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="@dimen/notification_importance_button_separation"
+                android:padding="@dimen/notification_importance_button_padding"
+                android:clickable="true"
+                android:focusable="true"
+                android:background="@drawable/notification_guts_priority_button_bg"
+                android:orientation="vertical"
+                android:visibility="gone">
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    android:gravity="center"
+                    >
+                    <ImageView
+                        android:id="@+id/automatic_icon"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:src="@drawable/ic_notifications_automatic"
+                        android:background="@android:color/transparent"
+                        android:tint="@color/notification_guts_priority_contents"
+                        android:clickable="false"
+                        android:focusable="false"/>
+                    <TextView
+                        android:id="@+id/automatic_label"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+                        android:layout_weight="1"
+                        android:ellipsize="end"
+                        android:maxLines="1"
+                        android:clickable="false"
+                        android:focusable="false"
+                        android:textAppearance="@style/TextAppearance.NotificationImportanceButton"
+                        android:text="@string/notification_automatic_title"/>
+                </LinearLayout>
+                <TextView
+                    android:id="@+id/automatic_summary"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/notification_importance_button_description_top_margin"
+                    android:visibility="gone"
+                    android:text="@string/notification_channel_summary_automatic"
+                    android:clickable="false"
+                    android:focusable="false"
+                    android:ellipsize="end"
+                    android:maxLines="2"
+                    android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
+            </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
+
+            <com.android.systemui.statusbar.notification.row.ButtonLinearLayout
+                android:id="@+id/alert"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:padding="@dimen/notification_importance_button_padding"
+                android:clickable="true"
+                android:focusable="true"
+                android:background="@drawable/notification_guts_priority_button_bg"
+                android:orientation="vertical">
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    android:gravity="center"
+                    >
+                    <ImageView
+                        android:id="@+id/alert_icon"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:src="@drawable/ic_notifications_alert"
+                        android:background="@android:color/transparent"
+                        android:tint="@color/notification_guts_priority_contents"
+                        android:clickable="false"
+                        android:focusable="false"/>
+                    <TextView
+                        android:id="@+id/alert_label"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+                        android:layout_weight="1"
+                        android:ellipsize="end"
+                        android:maxLines="1"
+                        android:clickable="false"
+                        android:focusable="false"
+                        android:textAppearance="@style/TextAppearance.NotificationImportanceButton"
+                        android:text="@string/notification_alert_title"/>
+                </LinearLayout>
+                <TextView
+                    android:id="@+id/alert_summary"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/notification_importance_button_description_top_margin"
+                    android:visibility="gone"
+                    android:text="@string/notification_channel_summary_default"
+                    android:clickable="false"
+                    android:focusable="false"
+                    android:ellipsize="end"
+                    android:maxLines="2"
+                    android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
+            </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
+
+            <com.android.systemui.statusbar.notification.row.ButtonLinearLayout
+                android:id="@+id/silence"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/notification_importance_button_separation"
+                android:padding="@dimen/notification_importance_button_padding"
+                android:clickable="true"
+                android:focusable="true"
+                android:background="@drawable/notification_guts_priority_button_bg"
+                android:orientation="vertical">
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    android:gravity="center"
+                    >
+                    <ImageView
+                        android:id="@+id/silence_icon"
+                        android:src="@drawable/ic_notifications_silence"
+                        android:background="@android:color/transparent"
+                        android:tint="@color/notification_guts_priority_contents"
+                        android:layout_gravity="center"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:clickable="false"
+                        android:focusable="false"/>
+                    <TextView
+                        android:id="@+id/silence_label"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:ellipsize="end"
+                        android:maxLines="1"
+                        android:clickable="false"
+                        android:focusable="false"
+                        android:layout_toEndOf="@id/silence_icon"
+                        android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+                        android:textAppearance="@style/TextAppearance.NotificationImportanceButton"
+                        android:text="@string/notification_silence_title"/>
+                </LinearLayout>
+                <TextView
+                    android:id="@+id/silence_summary"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/notification_importance_button_description_top_margin"
+                    android:visibility="gone"
+                    android:text="@string/notification_channel_summary_low"
+                    android:clickable="false"
+                    android:focusable="false"
+                    android:ellipsize="end"
+                    android:maxLines="2"
+                    android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
+            </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
+
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minHeight="60dp"
+            android:gravity="center_vertical"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            >
+            <TextView
+                android:id="@+id/promoted_demote"
+                android:text="@string/notification_inline_disable_promotion"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentStart="true"
+                android:gravity="start|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="200dp"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+            <TextView
+                android:id="@+id/promoted_dismiss"
+                android:text="@string/notification_inline_dismiss"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:gravity="end|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="125dp"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/bottom_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minHeight="60dp"
+            android:gravity="center_vertical"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            >
+            <TextView
+                android:id="@+id/turn_off_notifications"
+                android:text="@string/inline_turn_off_notifications"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentStart="true"
+                android:gravity="start|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="200dp"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+            <TextView
+                android:id="@+id/done"
+                android:text="@string/inline_ok_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:gravity="end|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="125dp"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+        </RelativeLayout>
+    </LinearLayout>
+</com.android.systemui.statusbar.notification.row.PromotedNotificationInfo>
diff --git a/packages/SystemUI/res/layout/shade_carrier.xml b/packages/SystemUI/res/layout/shade_carrier.xml
index 0fed393..6a5df9c 100644
--- a/packages/SystemUI/res/layout/shade_carrier.xml
+++ b/packages/SystemUI/res/layout/shade_carrier.xml
@@ -33,7 +33,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_weight="1"
-        android:textAppearance="@style/TextAppearance.QS.Status.Carriers"
+        android:textAppearance="@style/TextAppearance.QS.Status"
         android:textDirection="locale"
         android:marqueeRepeatLimit="marquee_forever"
         android:singleLine="true"
diff --git a/packages/SystemUI/res/layout/shade_carrier_group.xml b/packages/SystemUI/res/layout/shade_carrier_group.xml
index 2e8f98c..6551f3b 100644
--- a/packages/SystemUI/res/layout/shade_carrier_group.xml
+++ b/packages/SystemUI/res/layout/shade_carrier_group.xml
@@ -32,7 +32,7 @@
         android:minWidth="48dp"
         android:minHeight="48dp"
         android:gravity="center_vertical"
-        android:textAppearance="@style/TextAppearance.QS.Status.Carriers.NoCarrierText"
+        android:textAppearance="@style/TextAppearance.QS.Status"
         android:textDirection="locale"
         android:marqueeRepeatLimit="marquee_forever"
         android:singleLine="true"
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider.xml b/packages/SystemUI/res/layout/volume_dialog_slider.xml
index c5f468e..2628f49 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider.xml
@@ -18,14 +18,10 @@
     android:layout_height="match_parent"
     android:maxHeight="@dimen/volume_dialog_slider_height">
 
-    <com.google.android.material.slider.Slider
+    <androidx.compose.ui.platform.ComposeView
         android:id="@+id/volume_dialog_slider"
-        style="@style/SystemUI.Material3.Slider.Volume"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center"
-        android:layout_marginTop="-20dp"
-        android:layout_marginBottom="-20dp"
-        android:orientation="vertical"
-        android:theme="@style/Theme.Material3.DayNight" />
+        android:orientation="vertical" />
 </FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 91d92a2..e7efdba 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Sluitskermlegstukke"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Om ’n app met ’n legstuk oop te maak, sal jy moet verifieer dat dit jy is. Hou ook in gedagte dat enigeen dit kan bekyk, selfs wanneer jou tablet gesluit is. Sommige legstukke is moontlik nie vir jou sluitskerm bedoel nie en dit kan onveilig wees om dit hier by te voeg."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Het dit"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Legstukke"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Maak seker dat “Wys legstukke op sluitskerm” in instellings geaktiveer is om die “Legstukke”-kortpad by te voeg."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Instellings"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Wys sluimerskermknoppie"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Dateer tans op"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Ouerkontroles"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Jy sal nie jou volgende wekker <xliff:g id="WHEN">%1$s</xliff:g> hoor nie"</string>
     <string name="alarm_template" msgid="2234991538018805736">"om <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Word aan die bokant van gesprekskennisgewings en as \'n profielfoto op sluitskerm gewys, verskyn as \'n borrel, onderbreek Moenie Steur Nie"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> steun nie gesprekskenmerke nie"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Hierdie kennisgewings kan nie gewysig word nie."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Oproepkennisgewings kan nie gewysig word nie."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Hierdie groep kennisgewings kan nie hier opgestel word nie"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalender"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Sakrekenaar"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Moenie Steur Nie"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Volumeknoppieskortpad"</string>
     <string name="battery" msgid="769686279459897127">"Battery"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Stelselapps"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Verrigting van veelvuldige take"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Verdeelde skerm"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Toeganklikheid"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appkortpaaie"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Jy het die Bekyk Onlangse Apps-gebaar voltooi."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Swiep op en hou met drie vingers op jou raakpaneel om onlangse apps te bekyk"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Wissel apps"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Uitstekende werk!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Jy het die “wissel tussen apps”-gebaar voltooi."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Bekyk alle apps"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk die handelingsleutel op jou sleutelbord"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 6e079a2..76a38c9 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"የማያ ገፅ ቁልፍ ምግብሮች"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ምግብር በመጠቀም መተግበሪያ ለመክፈት እርስዎ መሆንዎን ማረጋገጥ አለብዎት። እንዲሁም የእርስዎ ጡባዊ በተቆለፈበት ጊዜ እንኳን ማንኛውም ሰው እነሱን ማየት እንደሚችል ከግምት ውስጥ ያስገቡ። አንዳንድ ምግብሮች ለማያ ገፅ ቁልፍዎ የታሰቡ ላይሆኑ ይችላሉ እና እዚህ ለማከል አስተማማኝ ላይሆኑ ይችላሉ።"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ገባኝ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ምግብሮች"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"የ«ምግብሮች» አቋራጭን ለማከል በቅንብሮች ውስጥ «ምግብሮችን በማያ ገፅ ቁልፍ ላይ አሳይ» የሚለው መንቃቱን ያረጋግጡ።"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ቅንብሮች"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"የገፀ ማያ አሳራፊ አዝራርን አሳይ"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ወደታች ተጎታች ምናሌ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"በማዘመን ላይ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"የስራ መገለጫ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"የአውሮፕላን ሁነታ"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"የወላጅ መቆጣጠሪያዎች"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"የእርስዎን ቀጣይ ማንቂያ <xliff:g id="WHEN">%1$s</xliff:g> አይሰሙም"</string>
     <string name="alarm_template" msgid="2234991538018805736">"በ<xliff:g id="WHEN">%1$s</xliff:g> ላይ"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"በ<xliff:g id="WHEN">%1$s</xliff:g> ላይ"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"በውይይት ማሳወቂያዎች አናት ላይ እና በማያ ገፅ መቆለፊያ ላይ እንደ መገለጫ ምስል ይታያል፣ እንደ አረፋ ሆኖ ይታያል፣ አትረብሽን ያቋርጣል"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ቅድሚያ"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> የውይይት ባህሪያትን አይደግፍም"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"እነዚህ ማሳወቂያዎች ሊሻሻሉ አይችሉም።"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"የጥሪ ማሳወቂያዎች ሊቀየሩ አይችሉም።"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"የማሳወቂያዎች ይህ ቡድን እዚህ ላይ ሊዋቀር አይችልም"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"የቀን መቁጠሪያ"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"ሒሳብ ማስያ"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"ካርታዎች"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"አትረብሽ"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"የድምፅ አዝራሮች አቋራጭ"</string>
     <string name="battery" msgid="769686279459897127">"ባትሪ"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"የሥርዓት መተግበሪያዎች"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ብዙ ተግባራትን በተመሳሳይ ጊዜ ማከናወን"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"የተከፈለ ማያ ገፅ"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ተደራሽነት"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ግብዓት"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"የመተግበሪያ አቋራጮች"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"የአሁን መተግበሪያ"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"የቅርብ ጊዜ መተግበሪያዎች አሳይ ምልክትን አጠናቅቀዋል።"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"የቅርብ ጊዜ መተግበሪያዎችን ለማየት የመዳሰሻ ሰሌዳዎ ላይ ሦስት ጣቶችን በመጠቀም ወደላይ ያንሸራትቱ እና ይያዙ"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"መተግበሪያዎችን ይቀያይሩ"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ጥሩ ሠርተዋል!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"የመተግበሪያ ምልክቶችን ይቀይሩ የሚለወን አጠናቅቀዋል።"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"ሁሉንም መተግበሪያዎች ይመልከቱ"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"በቁልፍ ሰሌዳዎ ላይ ያለውን የተግባር ቁልፍ ይጫኑ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 7896cdb..7db9a0d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"التطبيقات المصغّرة المصمَّمة لشاشة القفل"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"لفتح تطبيق باستخدام تطبيق مصغَّر، عليك إثبات هويتك. يُرجى ملاحظة أنّ أي شخص يمكنه الاطّلاع محتوى التطبيقات المصغَّرة، حتى وإن كان جهازك اللوحي مُقفلاً. بعض التطبيقات المصغّرة قد لا تكون مُصمَّمة لإضافتها إلى شاشة القفل، وقد يكون هذا الإجراء غير آمن."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"حسنًا"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"التطبيقات المصغَّرة"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"لإضافة اختصار \"التطبيقات المصغّرة\"، يجب تفعيل خيار \"عرض التطبيقات المصغّرة على شاشة القفل\" من خلال الإعدادات."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"الإعدادات"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"زر \"إظهار شاشة الاستراحة\""</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تبديل المستخدم"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"القائمة المنسدلة"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string>
@@ -802,6 +805,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"تظهر في أعلى إشعارات المحادثات وكصورة ملف شخصي على شاشة القفل وتظهر على شكل فقاعة لمقاطعة ميزة \"عدم الإزعاج\"."</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"الأولوية"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"لا يدعم تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> ميزات المحادثات."</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"يتعذّر تعديل هذه الإشعارات."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"لا يمكن تعديل إشعارات المكالمات."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"يتعذّر ضبط مجموعة الإشعارات هذه هنا."</string>
@@ -912,6 +919,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"‏تقويم Google"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"الآلة الحاسبة"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"‏خرائط Google"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"عدم الإزعاج"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"اختصار أزرار مستوى الصوت"</string>
     <string name="battery" msgid="769686279459897127">"البطارية"</string>
@@ -1497,11 +1514,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"لقد أكملْت التدريب على إيماءة عرض التطبيقات المستخدَمة مؤخرًا."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"لعرض التطبيقات المستخدَمة مؤخرًا، يُرجى التمرير سريعًا للأعلى مع الاستمرار باستخدام ثلاثة أصابع على لوحة اللمس"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"التبديل بين التطبيقات"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"أحسنت صنعًا."</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"أكملت التدريب على إيماءة التبديل بين التطبيقات."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"عرض جميع التطبيقات"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اضغط على مفتاح الإجراء في لوحة المفاتيح"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 7c99df9..297b410 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্ৰীন ৱিজেট"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"এটা ৱিজেট ব্যৱহাৰ কৰি কোনো এপ্ খুলিবলৈ, এয়া আপুনিয়েই বুলি সত্যাপন পৰীক্ষা কৰিব লাগিব। লগতে, মনত ৰাখিব যে যিকোনো লোকেই সেইবোৰ চাব পাৰে, আনকি আপোনাৰ টেবলেটটো লক হৈ থাকিলেও। কিছুমান ৱিজেট হয়তো আপোনাৰ লক স্ক্ৰীনৰ বাবে কৰা হোৱা নাই আৰু ইয়াত যোগ কৰাটো অসুৰক্ষিত হ’ব পাৰে।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুজি পালোঁ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ৱিজেট"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ৱিজেট\"ৰ শ্বৰ্টকাট যোগ দিবলৈ, ছেটিঙত \"লক স্ক্ৰীনত ৱিজেট দেখুৱাওক\" সক্ষম কৰি থোৱাটো নিশ্চিত কৰক।"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ছেটিং"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"স্ক্ৰীনছেভাৰৰ বুটাম দেখুৱাওক"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যৱহাৰকাৰী সলনি কৰক"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুল-ডাউনৰ মেনু"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ আটাইবোৰ এপ্ আৰু ডেটা মচা হ\'ব।"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"আপডে’ট কৰি থকা হৈছে"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"এয়াৰপ্লে’ন ম’ড"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"অভিভাৱকীয় নিয়ন্ত্ৰণ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"আপুনি আপোনাৰ পিছৰটো এলাৰ্ম <xliff:g id="WHEN">%1$s</xliff:g> বজাত শুনা নাপাব"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"বাৰ্তালাপৰ জাননীৰ শীৰ্ষত আৰু প্ৰ’ফাইল চিত্ৰ হিচাপে লক স্ক্ৰীনত দেখুৱায়, এটা বাবল হিচাপে দেখা পোৱা যায়, অসুবিধা নিদিব ম’ডত ব্যাঘাত জন্মায়"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"অগ্ৰাধিকাৰ"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বাৰ্তালাপৰ সুবিধাসমূহ সমৰ্থন নকৰে"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"এই জাননীসমূহ সংশোধন কৰিব নোৱাৰি।"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"কলৰ জাননীসমূহ সংশোধন কৰিব নোৱাৰি।"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"এই ধৰণৰ জাননীবোৰ ইয়াত কনফিগাৰ কৰিব পৰা নাযায়"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"কেলকুলেটৰ"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"মেপ"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"অসুবিধা নিদিব"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ভলিউম বুটামসমূহৰ শ্বৰ্টকাট"</string>
     <string name="battery" msgid="769686279459897127">"বেটাৰী"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ছিষ্টেম এপ্‌"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"বিভাজিত স্ক্ৰীন"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"সাধ্য সুবিধা"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ইনপুট"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"এপ্ শ্বৰ্টকাটসমূহ"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"বৰ্তমানৰ এপ্‌"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"আপুনি শেহতীয়া এপ্ চোৱাৰ নিৰ্দেশনাটো সম্পূৰ্ণ কৰিছে।"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"শেহতীয়া এপ্‌সমূহ চাবলৈ, আপোনাৰ টাচ্চপেডত তিনিটা আঙুলি ব্যৱহাৰ কৰি ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"এপ্‌সমূহ সলনি কৰক"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"বঢ়িয়া!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"আপুনি এপ্‌ সলনি কৰাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"আটাইবোৰ এপ্ চাওক"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 64b2726..fb13ac4 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kilid ekranı vidcetləri"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Vidcetdən istifadə edərək tətbiqi açmaq üçün kimliyi doğrulamalısınız. Planşet kilidli olsa da, hər kəs vidcetlərə baxa bilər. Bəzi vidcetlər kilid ekranı üçün nəzərdə tutulmayıb və bura əlavə etmək təhlükəli ola bilər."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidcetlər"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Vidcetlər\" qısayolunu əlavə etmək üçün ayarlarda \"Vidcetləri kilidli ekranda göstərin\" seçimi aktiv olmalıdır."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ayarlar"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Ekran qoruyucu düyməsini göstərin"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aşağı çəkilən menyu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Güncəllənir"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Təyyarə rejimi"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Valideyn nəzarətləri"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> zaman növbəti xəbərdarlığınızı eşitməyəcəksiniz"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Söhbət bildirişlərinin yuxarısında və kilid ekranında profil şəkli kimi göstərilir, baloncuq kimi görünür, Narahat Etməyin rejimini kəsir"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> söhbət funksiyalarını dəstəkləmir"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Bu bildirişlər dəyişdirilə bilməz."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Zəng bildirişləri dəyişdirilə bilməz."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Bu bildiriş qrupunu burada konfiqurasiya etmək olmaz"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Təqvim"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulyator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Xəritə"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Narahat Etməyin"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Səs düymələri qısayolu"</string>
     <string name="battery" msgid="769686279459897127">"Batareya"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistem tətbiqləri"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoxsaylı tapşırıq icrası"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Bölünmüş ekran"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Xüsusi imkanlar"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Daxiletmə"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Tətbiq qısayolları"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Cari tətbiq"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Son tətbiqlərə baxmaq jestini tamamladınız."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Ən son tətbiqlərə baxmaq üçün taçpeddə üç barmağınızla yuxarı çəkin və saxlayın"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Başqa tətbiqə keçin"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Əla!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Tətbiqlərarası keçid jestini tamamladınız."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Bütün tətbiqlərə baxın"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturada fəaliyyət açarına basın"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 3156692..be65e9e 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti za zaključani ekran"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju koja koristi vidžet, treba da potvrdite da ste to vi. Imajte u vidu da svako može da ga vidi, čak i kada je tablet zaključan. Neki vidžeti možda nisu namenjeni za zaključani ekran i možda nije bezbedno da ih tamo dodate."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Važi"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidžeti"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da biste dodali prečicu Vidžeti, uverite se da je u podešavanjima omogućeno Prikazuj vidžete na zaključanom ekranu."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Podešavanja"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Dugme Prikaži čuvar ekrana"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Istražite režim centra"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Pristupajte omiljenim vidžetima i čuvarima ekrana tokom punjenja."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Idemo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ažurira se"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Režim rada u avionu"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Roditeljski nadzor"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sledeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Prikazuje se u vrhu obaveštenja o konverzacijama i kao slika profila na zaključanom ekranu, pojavljuje se kao oblačić, prekida režim Ne uznemiravaj"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritetno"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ne podržava funkcije konverzacije"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ova obaveštenja ne mogu da se menjaju."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Obaveštenja o pozivima ne mogu da se menjaju."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ova grupa obaveštenja ne može da se konfiguriše ovde"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Mape"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ne uznemiravaj"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Prečica za dugmad za jačinu zvuka"</string>
     <string name="battery" msgid="769686279459897127">"Baterija"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka istovremeno"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podeljeni ekran"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Pristupačnost"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice za aplikacije"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelna aplikacija"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Dovršili ste pokret za prikazivanje nedavno korišćenih aplikacija."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Da biste pregledali nedavne aplikacije, prevucite nagore i zadržite sa tri prsta na tačpedu"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Pređi na drugu aplikaciju"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Prevucite udesno sa četiri prsta na tačpedu"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Odlično!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Dovršili ste pokret za promenu aplikacija."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Prevucite ulevo sa četiri prsta na tačpedu da biste prešli na drugu aplikaciju"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Prikaži sve aplikacije"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite taster radnji na tastaturi"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 5e339a4..ae73144 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Віджэты на экране блакіроўкі"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Каб адкрыць праграму з дапамогай віджэта, вам неабходна будзе пацвердзіць сваю асобу. Таксама памятайце, што такія віджэты могуць пабачыць іншыя людзі, нават калі экран планшэта заблакіраваны. Некаторыя віджэты могуць не падыходзіць для выкарыстання на экране блакіроўкі, і дадаваць іх сюды можа быць небяспечна."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Зразумела"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Віджэты"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Каб дадаць спалучэнне клавіш \"Віджэты\", у наладах павінна быць уключана функцыя \"Паказваць віджэты на экране блакіроўкі\"."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Налады"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Кнопка \"Паказаць застаўку\""</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Перайсці да іншага карыстальніка"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"высоўнае меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Абнаўленне…"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Працоўны профіль"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Рэжым палёту"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Бацькоўскі кантроль"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Вы не пачуеце наступны будзільнік <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"З’яўляецца ўверсе раздзела размоў як усплывальнае апавяшчэнне, якое перарывае рэжым \"Не турбаваць\" і паказвае на экране блакіроўкі відарыс профілю"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Прыярытэт"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не падтрымлівае функцыі размовы"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Гэтыя апавяшчэнні нельга змяніць."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Апавяшчэнні пра выклікі нельга змяніць."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Тут канфігурыраваць гэту групу апавяшчэнняў забаронена"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Каляндар"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Калькулятар"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Карты"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Не турбаваць"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Доступ праз кнопкі рэгулявання гучнасці"</string>
     <string name="battery" msgid="769686279459897127">"Акумулятар"</string>
@@ -1497,11 +1513,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Вы скончылі вывучэнне жэсту для прагляду нядаўніх праграм."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Каб праглядзець нядаўнія праграмы, правядзіце трыма пальцамі ўверх па сэнсарным экране і затрымайце пальцы"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Пераключэнне праграм"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Выдатная праца!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Вы навучыліся рабіць жэст пераключэння праграм."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Глядзець усе праграмы"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Націсніце клавішу дзеяння на клавіятуры"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 245711f..db253db 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Приспособления за заключения екран"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите дадено приложение посредством приспособление, ще трябва да потвърдите, че това сте вие. Също така имайте предвид, че всеки ще вижда приспособленията дори когато таблетът ви е заключен. Възможно е някои от тях да не са предназначени за заключения екран и добавянето им на него може да е опасно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Разбрах"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Приспособления"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"За да добавите пряк път към „Приспособления“, уверете се, че опцията „Показване на приспособленията на заключения екран“ е активирана в настройките."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Настройки"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Бутон за показване на скрийнсейвъра"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Превключване между потребителите"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падащо меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Актуализира се"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Потребителски профил в Work"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Самолетен режим"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Родителски контроли"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Няма да чуете следващия си будилник в <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"в <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"в/ъв <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Показва се в горната част на известията за разговори и като снимка на потребителския профил на заключения екран, изглежда като балонче, прекъсва режима „Не безпокойте“"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддържа функциите за разговор"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Тези известия не могат да бъдат променяни."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Известията за обаждания не могат да бъдат променяни."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Тази група от известия не може да бъде конфигурирана тук"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Календар"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Калкулатор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Карти"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Не безпокойте"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Пряк път към бутоните за силата на звука"</string>
     <string name="battery" msgid="769686279459897127">"Батерия"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системни приложения"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Няколко задачи едновременно"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Разделен екран"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Достъпност"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Въвеждане"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Преки пътища към приложения"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Текущо приложение"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Изпълнихте жеста за преглед на скорошните приложения."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"За да прегледате скорошните приложения, плъзнете три пръста нагоре по сензорния панел и задръжте"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Превключване на приложенията"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Отлично!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Изпълнихте жеста за превключване между приложения."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Преглед на всички приложения"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натиснете клавиша за действия на клавиатурата си"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 42a4d54..5e9860d 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্রিন উইজেট"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"উইজেট ব্যবহার করে কোনও অ্যাপ খুলতে, আপনাকে নিজের পরিচয় যাচাই করতে হবে। এছাড়াও, মনে রাখবেন, আপনার ট্যাবলেট লক থাকলেও যেকেউ তা দেখতে পারবেন। কিছু উইজেট আপনার লক স্ক্রিনের উদ্দেশ্যে তৈরি করা হয়নি এবং এখানে যোগ করা নিরাপদ নাও হতে পারে।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুঝেছি"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"উইজেট"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"উইজেট\" শর্টকার্ট যোগ করতে, সেটিংস থেকে \"লক স্ক্রিনে উইজেট দেখুন\" বিকল্প চালু আছে কিনা তা নিশ্চিত করুন।"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"সেটিংস"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"স্ক্রিন সেভার বোতাম দেখুন"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুলডাউন মেনু"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ ও ডেটা মুছে ফেলা হবে।"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"আপডেট করা হচ্ছে"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"কাজের প্রোফাইল"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"বিমান মোড"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"অভিভাবকীয় নিয়ন্ত্রণ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"আপনি আপনার পরবর্তী <xliff:g id="WHEN">%1$s</xliff:g> অ্যালার্ম শুনতে পাবেন না"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> -তে"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়, বাবল হিসেবেও এটি দেখা যায় এবং এর ফলে \'বিরক্ত করবে না\' মোডে কাজ করতে অসুবিধা হয়"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"অগ্রাধিকার"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এ কথোপকথন ফিচার কাজ করে না"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"এই বিজ্ঞপ্তিগুলি পরিবর্তন করা যাবে না।"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"কল বিজ্ঞপ্তি পরিবর্তন করা যাবে না।"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"এই সমস্ত বিজ্ঞপ্তিকে এখানে কনফিগার করা যাবে না"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"ক্যালকুলেটর"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"ম্যাপ"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"বিরক্ত করবে না"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ভলিউম বোতামের শর্টকাট"</string>
     <string name="battery" msgid="769686279459897127">"ব্যাটারি"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"সিস্টেম অ্যাপ"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"স্প্লিট স্ক্রিন"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"অ্যাক্সেসিবিলিটি"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ইনপুট"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"অ্যাপ শর্টকাট"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"বর্তমান অ্যাপ"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপের জেসচার দেখা সম্পূর্ণ করেছেন।"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"সাম্প্রতিক অ্যাপ দেখতে, নিজের টাচপ্যাডে তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করে হোল্ড করুন"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"অ্যাপ পরিবর্তন করুন"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"অসাধারণ!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"আপনি অ্যাপ পরিবর্তন করার জেসচার সম্পূর্ণ করেছেন।"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"সব অ্যাপ দেখুন"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপনার কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 69f1635..1b79810 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti na zaključanom ekranu"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da otvorite aplikaciju pomoću vidžeta, morat ćete potvrditi identitet. Također imajte na umu da ih svako može pregledati, čak i ako je tablet zaključan. Neki vidžeti možda nisu namijenjeni za vaš zaključani ekran i njihovo dodavanje ovdje možda nije sigurno."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumijem"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidžeti"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da dodate prečicu \"Vidžeti\", provjerite je li u postavkama omogućeno \"Prikazuj vidžete na zaključanom ekranu\"."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Postavke"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Dugme za prikaz čuvara ekrana"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Istraži način Huba"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Pristupite omiljenim widgetima i čuvarima zaslona tijekom punjenja."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Započnimo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ažuriranje"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Radni profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u avionu"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Roditeljski nadzor"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Prikazuje se na vrhu obavještenja u razgovorima i kao slika profila na zaključanom ekranu, izgleda kao oblačić, prekida funkciju Ne ometaj"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritetno"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ne podržava funkcije razgovora"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ta obavještenja se ne mogu izmijeniti."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Nije moguće izmijeniti obavještenja o pozivima."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ovu grupu obavještenja nije moguće konfigurirati ovdje"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Mape"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ne ometaj"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Prečica za dugmad za Jačinu zvuka"</string>
     <string name="battery" msgid="769686279459897127">"Baterija"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podijeljeni ekran"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Pristupačnost"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice aplikacije"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvršili ste pokret za prikaz nedavnih aplikacija."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Da pogledate nedavne aplikacije, prevucite nagore i zadržite s tri prsta na dodirnoj podlozi"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Promijenite aplikaciju"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Prijeđite udesno četirima prstima na dodirnoj podlozi"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Sjajno!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Izvršili ste pokret za promjenu aplikacije."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Prijeđite udesno četirima prstima na dodirnoj podlozi za prebacivanje između aplikacija"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Pogledajte sve aplikacije"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku radnji na tastaturi"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index d90c4ff..eae1b34 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de la pantalla de bloqueig"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per obrir una aplicació utilitzant un widget, necessitaràs verificar la teva identitat. També has de tenir en compte que qualsevol persona pot veure els widgets, fins i tot quan la tauleta està bloquejada. És possible que alguns widgets no estiguin pensats per a la pantalla de bloqueig i que no sigui segur afegir-los-hi."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entesos"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Per afegir la drecera Widgets, assegura\'t que l\'opció Mostra els widgets a la pantalla de bloqueig estigui activada a la configuració."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configuració"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botó Mostra l\'estalvi de pantalla"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"S\'està actualitzant"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de treball"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mode d\'avió"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Controls parentals"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> no sentiràs la pròxima alarma"</string>
     <string name="alarm_template" msgid="2234991538018805736">"Hora: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"Dia: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Es mostra a la part superior de les notificacions de les converses i com a foto de perfil a la pantalla de bloqueig, apareix com una bombolla, interromp el mode No molestis"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritat"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admet les funcions de converses"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Aquestes notificacions no es poden modificar."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Les notificacions de trucades no es poden modificar."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Aquest grup de notificacions no es pot configurar aquí"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"No molestis"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Drecera per als botons de volum"</string>
     <string name="battery" msgid="769686279459897127">"Bateria"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicacions del sistema"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasca"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accessibilitat"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Dreceres d\'aplicacions"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicació actual"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completat el gest per veure les aplicacions recents."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Per veure les aplicacions recents, llisca cap amunt amb tres dits i mantén-los premuts al ratolí tàctil"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Canviar d\'aplicació"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Ben fet!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Has completat el gest per canviar d\'aplicació."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Mostra totes les aplicacions"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prem la tecla d\'acció al teclat"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 2cfc4da..9422804 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgety na obrazovce uzamčení"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"K otevření aplikace pomocí widgetu budete muset ověřit svou totožnost. Také mějte na paměti, že widgety uvidí kdokoli, i když tablet bude uzamčen. Některé widgety nemusí být pro obrazovku uzamčení určeny a nemusí být bezpečné je na ni přidat."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Rozumím"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgety"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pokud chcete přidat zkratku Widgety, zapněte v nastavení možnost Zobrazovat widgety na obrazovce uzamčení."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavení"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Zobrazit tlačítko spořiče obrazovky"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Prozkoumejte Režim centra"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Mějte po ruce oblíbené widgety a spořiče obrazovky při nabíjení."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Jdeme na to"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbalovací nabídka"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
@@ -802,6 +802,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Zobrazuje se v horní části sekce konverzací a na obrazovce uzamčení se objevuje jako profilová fotka, má podobu bubliny a deaktivuje režim Nerušit"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritní"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> funkce konverzace nepodporuje"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Tato oznámení nelze upravit."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Upozornění na hovor nelze upravit."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Tuto skupinu oznámení tady nelze nakonfigurovat"</string>
@@ -912,6 +916,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendář"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulačka"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Mapy"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Nerušit"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Zkratka tlačítek hlasitosti"</string>
     <string name="battery" msgid="769686279459897127">"Baterie"</string>
@@ -1497,12 +1511,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Provedli jste gesto pro zobrazení nedávných aplikací."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Pokud chcete zobrazit poslední aplikace, přejeďte na touchpadu třemi prsty nahoru a podržte je"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Přepnout aplikace"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Přejeďte po touchpadu čtyřmi prsty vpravo"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Výborně!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Dokončili jste gesto přepínání aplikací."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Přejetím čtyřmi prsty po touchpadu doprava přepněte aplikace"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Zobrazit všechny aplikace"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stiskněte akční klávesu na klávesnici"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Výborně!"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 29961e3..cb70b9a 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets på låseskærmen"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Hvis du vil åbne en app ved hjælp af en widget, skal du verificere din identitet. Husk også, at alle kan se dem, også når din tablet er låst. Nogle widgets er muligvis ikke beregnet til låseskærmen, og det kan være usikkert at tilføje dem her."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Hvis du vil tilføje genvejen \"Widgets\", skal du sørge for, at \"Vis widgets på låseskærmen\" er aktiveret i indstillingerne."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Indstillinger"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Knappen Vis pauseskærm"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullemenu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Opdaterer"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Arbejdsprofil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flytilstand"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Børnesikring"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Du vil ikke kunne høre din næste alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"på <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Vises øverst i samtalenotifikationer og som et profilbillede på låseskærmen. Vises som en boble, der afbryder Forstyr ikke"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> understøtter ikke samtalefunktioner"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Disse notifikationer kan ikke redigeres."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Opkaldsnotifikationer kan ikke redigeres."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Du kan ikke konfigurere denne gruppe notifikationer her"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalender"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Lomme­regner"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Forstyr ikke"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Genvej til lydstyrkeknapper"</string>
     <string name="battery" msgid="769686279459897127">"Batteri"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemapps"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Opdelt skærm"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Hjælpefunktioner"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appgenveje"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuel app"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du har udført bevægelsen for at se de seneste apps."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Du kan se nyligt brugte apps ved at stryge opad og holde tre fingre nede på touchpladen"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Skift mellem apps"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Godt klaret!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Du har fuldført bevægelsen for at skifte mellem apps."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Se alle apps"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryk på handlingstasten på dit tastatur"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 2e452dd..09c7067 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Sperrbildschirm-Widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Wenn du eine App mit einem Widget öffnen möchtest, musst du deine Identität bestätigen. Beachte auch, dass jeder die Widgets sehen kann, auch wenn dein Tablet gesperrt ist. Einige Widgets sind möglicherweise nicht für den Sperrbildschirm vorgesehen, sodass es unsicher sein kann, sie hier hinzuzufügen."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Zum Hinzufügen der Verknüpfung „Widgets“ musst du zuerst in den Einstellungen die Option „Widgets auf Sperrbildschirm zeigen“ aktivieren."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Einstellungen"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Schaltfläche „Bildschirmschoner anzeigen“"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Pull-down-Menü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Wird aktualisiert…"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Arbeitsprofil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flugmodus"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Jugendschutzeinstellungen"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Lautloser Weckruf <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"um <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"am <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Wird oben im Bereich „Unterhaltungen“ sowie als Profilbild auf dem Sperrbildschirm angezeigt, erscheint als Bubble, unterbricht „Bitte nicht stören“"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Priorität"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> unterstützt keine Funktionen für Unterhaltungen"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Diese Benachrichtigungen können nicht geändert werden."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Anrufbenachrichtigungen können nicht geändert werden."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Die Benachrichtigungsgruppe kann hier nicht konfiguriert werden"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalender"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Rechner"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Bitte nicht stören"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Tastenkombination für Lautstärketasten"</string>
     <string name="battery" msgid="769686279459897127">"Akku"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System-Apps"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Splitscreen"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Bedienungshilfen"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Eingabe"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Tastaturkürzel für Apps"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelle App"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du hast das Tutorial für die Touch-Geste zum Aufrufen der zuletzt verwendeten Apps abgeschlossen."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Wenn du zuletzt verwendete Apps aufrufen möchtest, wische mit drei Fingern nach oben und halte das Touchpad gedrückt"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Zwischen Apps wechseln"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Gut gemacht!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Du hast die Touch-Geste „Zwischen Apps wechseln\" abgeschlossen."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle Apps anzeigen"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Drücke die Aktionstaste auf deiner Tastatur"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index c5aa667..cc4ce17 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Γραφικά στοιχεία οθόνης κλειδώματος"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Για να ανοίξετε μια εφαρμογή χρησιμοποιώντας ένα γραφικό στοιχείο, θα πρέπει να επαληθεύσετε την ταυτότητά σας. Επίσης, λάβετε υπόψη ότι η προβολή τους είναι δυνατή από οποιονδήποτε, ακόμα και όταν το tablet σας είναι κλειδωμένο. Ορισμένα γραφικά στοιχεία μπορεί να μην προορίζονται για την οθόνη κλειδώματος και η προσθήκη τους εδώ ενδέχεται να μην είναι ασφαλής."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Το κατάλαβα"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Για να προσθέσετε τη συντόμευση Γραφικά στοιχεία, βεβαιωθείτε ότι η ρύθμιση Εμφάνιση γραφικών στοιχείων στην οθόνη κλειδώματος είναι ενεργοποιημένη στις ρυθμίσεις."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ρυθμίσεις"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Εμφάνιση κουμπιού προφύλαξης οθόνης"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Εξερεύνηση της λειτουργίας Hub"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Αποκτήστε πρόσβαση στα αγαπημένα σας γραφικά στοιχεία και τις προφυλάξεις οθόνης κατά τη φόρτιση."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Ας ξεκινήσουμε"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"αναπτυσσόμενο μενού"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ενημέρωση"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Προφίλ εργασίας"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Λειτουργία πτήσης"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Γονικοί έλεγχοι"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Δεν θα ακούσετε το επόμενο ξυπνητήρι σας <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"στις <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"στις <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Εμφανίζεται στην κορυφή των ειδοποιήσεων συζήτησης και ως φωτογραφία προφίλ στην οθόνη κλειδώματος, εμφανίζεται ως συννεφάκι, διακόπτει τη λειτουργία Μην ενοχλείτε"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Προτεραιότητα"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν υποστηρίζει τις λειτουργίες συζήτησης"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Δεν είναι δυνατή η τροποποίηση αυτών των ειδοποιήσεων"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Δεν είναι δυνατή η τροποποίηση των ειδοποιήσεων κλήσεων."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Δεν είναι δυνατή η διαμόρφωση αυτής της ομάδας ειδοποιήσεων εδώ"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Ημερολόγιο"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Αριθμομηχανή"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Χάρτες"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Μην ενοχλείτε"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Συντόμευση κουμπιών έντασης ήχου"</string>
     <string name="battery" msgid="769686279459897127">"Μπαταρία"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Εφαρμογές συστήματος"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Πολυδιεργασία"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Διαχωρισμός οθόνης"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Προσβασιμότητα"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Εισαγωγή"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Συντομεύσεις εφαρμογών"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Τρέχουσα εφαρμογή"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ολοκληρώσατε την κίνηση για την προβολή πρόσφατων εφαρμογών."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Για προβολή πρόσφατων εφαρμογών, σύρετε προς τα επάνω με τρία δάχτυλα και κρατήστε τα δάχτυλά σας στην επιφάνεια αφής"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Εναλλαγή μεταξύ εφαρμογών"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Σύρετε προς τα δεξιά με τέσσερα δάχτυλα στην επιφάνεια αφής"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Μπράβο!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ολοκληρώσατε την κίνηση εναλλαγής εφαρμογών."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Σύρετε προς τα δεξιά με τέσσερα δάχτυλα στην επιφάνεια αφής για εναλλαγή μεταξύ εφαρμογών"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Προβολή όλων των εφαρμογών"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Πατήστε το πλήκτρο ενέργειας στο πληκτρολόγιό σας"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Μπράβο!"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 4144dc3..c7ee5c5 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \'Widgets\' shortcut, make sure that \'Show widgets on lock screen\' is enabled in settings."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Show screensaver button"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Parental controls"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"at <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> doesn’t support conversation features"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"These notifications can\'t be modified."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Call notifications can\'t be modified."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"This group of notifications cannot be configured here"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Do Not Disturb"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Volume buttons shortcut"</string>
     <string name="battery" msgid="769686279459897127">"Battery"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accessibility"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"To view recent apps, swipe up and hold using three fingers on your touchpad"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Switch apps"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Well done!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"You completed the switch apps gesture."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index b00b01d..c26bee1 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you’ll need to verify it’s you. Also, keep in mind that anyone can view them, even when your tablet’s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \"Widgets\" shortcut, make sure \"Show widgets on lock screen\" is enabled in settings."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Show screensaver button"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Explore hub mode"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Access your favorite widgets and screen savers while charging."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Let’s go"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -801,6 +801,8 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> doesn’t support conversation features"</string>
+    <string name="notification_inline_dismiss" msgid="88423586921134258">"Dismiss"</string>
+    <string name="notification_inline_disable_promotion" msgid="6880961831026048166">"Don\'t show again"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"These notifications can\'t be modified."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Call notifications can\'t be modified."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"This group of notifications cannot be configured here"</string>
@@ -911,6 +913,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Do Not Disturb"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Volume buttons shortcut"</string>
     <string name="battery" msgid="769686279459897127">"Battery"</string>
@@ -1431,8 +1443,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accessibility"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current App"</string>
@@ -1496,10 +1507,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"To view recent apps, swipe up and hold using three fingers on your touchpad"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Switch apps"</string>
-    <string name="touchpad_switch_apps_gesture_guidance" msgid="2751565200937541667">"Swipe left using four fingers on your touchpad"</string>
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Swipe right using four fingers on your touchpad"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Great job!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"You completed the switch apps gesture."</string>
-    <string name="touchpad_switch_gesture_error_body" msgid="5508381152326379652">"Swipe left using four fingers on your touchpad to switch apps"</string>
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Swipe right using four fingers on your touchpad to switch apps"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 4144dc3..c7ee5c5 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \'Widgets\' shortcut, make sure that \'Show widgets on lock screen\' is enabled in settings."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Show screensaver button"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Parental controls"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"at <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> doesn’t support conversation features"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"These notifications can\'t be modified."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Call notifications can\'t be modified."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"This group of notifications cannot be configured here"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Do Not Disturb"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Volume buttons shortcut"</string>
     <string name="battery" msgid="769686279459897127">"Battery"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accessibility"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"To view recent apps, swipe up and hold using three fingers on your touchpad"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Switch apps"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Well done!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"You completed the switch apps gesture."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 4144dc3..c7ee5c5 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \'Widgets\' shortcut, make sure that \'Show widgets on lock screen\' is enabled in settings."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Show screensaver button"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Parental controls"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"at <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> doesn’t support conversation features"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"These notifications can\'t be modified."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Call notifications can\'t be modified."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"This group of notifications cannot be configured here"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Do Not Disturb"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Volume buttons shortcut"</string>
     <string name="battery" msgid="769686279459897127">"Battery"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accessibility"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"To view recent apps, swipe up and hold using three fingers on your touchpad"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Switch apps"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Well done!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"You completed the switch apps gesture."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 85ff751..95ebbb2 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets en la pantalla de bloqueo"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una app usando un widget, debes verificar tu identidad. Además, ten en cuenta que cualquier persona podrá verlo, incluso cuando la tablet esté bloqueada. Es posible que algunos widgets no se hayan diseñados para la pantalla de bloqueo y podría ser peligroso agregarlos allí."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para agregar el acceso directo de \"Widgets\", asegúrate de que la opción \"Mostrar widgets en la pantalla de bloqueo\" esté habilitada en la configuración."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configuración"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botón para mostrar el protector de pantalla"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú expandible"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Actualizando"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avión"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Controles parentales"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma a la(s) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"a la(s) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"el <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Aparece en forma de burbuja y como foto de perfil en la parte superior de las notificaciones de conversación, en la pantalla de bloqueo, y detiene el modo No interrumpir"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritaria"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admite funciones de conversación"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"No se pueden modificar estas notificaciones."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"No se pueden modificar las notificaciones de llamada."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"No se puede configurar aquí este grupo de notificaciones"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendario"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"No interrumpir"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Combinación de teclas de botones de volumen"</string>
     <string name="battery" msgid="769686279459897127">"Batería"</string>
@@ -1432,14 +1448,13 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps del sistema"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Tareas múltiples"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accesibilidad"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App actual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
-    <string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personalizar combinaciones de teclas"</string>
+    <string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personalizar combinaciones"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"¿Quieres quitar la combinación?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"¿Quieres restablecer la configuración predeterminada?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Para crear esta combinación de teclas, presiona la tecla de acción y una o más teclas"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaste el gesto para ver las apps recientes."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Para ver las apps recientes, desliza tres dedos hacia arriba y mantenlos presionados en el panel táctil"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Cambia de app"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"¡Bien hecho!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Completaste el gesto para cambiar de app."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las apps"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Presiona la tecla de acción en el teclado"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 62c6290..2c88f8e 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets para la pantalla de bloqueo"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una aplicación usando un widget, deberás verificar que eres tú. Además, ten en cuenta que cualquier persona podrá verlos, incluso aunque tu tablet esté bloqueada. Es posible que algunos widgets no estén pensados para la pantalla de bloqueo y no sea seguro añadirlos aquí."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para añadir el acceso directo Widgets, asegúrate de que la opción Mostrar widgets en la pantalla de bloqueo esté habilitada en los ajustes."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ajustes"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botón para mostrar el salvapantallas"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Actualizando"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo Avión"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Controles parentales"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
     <string name="alarm_template" msgid="2234991538018805736">"a las <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Se muestra encima de las notificaciones de conversaciones y como imagen de perfil en la pantalla de bloqueo, aparece como burbuja e interrumpe el modo No molestar"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioridad"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admite funciones de conversación"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Estas notificaciones no se pueden modificar."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Las notificaciones de llamada no se pueden modificar."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Este grupo de notificaciones no se puede configurar aquí"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendario"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"No molestar"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Acceso directo de los botones de volumen"</string>
     <string name="battery" msgid="769686279459897127">"Batería"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicaciones del sistema"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarea"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accesibilidad"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación en uso"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completado el gesto para ver las aplicaciones recientes."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Para ver las aplicaciones recientes, desliza tres dedos hacia arriba y mantén pulsado en el panel táctil"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Cambiar de aplicación"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"¡Bien hecho!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Has completado el gesto para cambiar de aplicación."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las aplicaciones"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pulsa la tecla de acción de tu teclado"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 920af5c..c96cc04 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lukustuskuva vidinad"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Rakenduse avamiseks vidina abil peate kinnitama, et see olete teie. Samuti pidage meeles, et kõik saavad vidinaid vaadata, isegi kui teie tahvelarvuti on lukus. Mõni vidin ei pruugi olla ette nähtud teie lukustuskuva jaoks ja seda pole turvaline siia lisada."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selge"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidinad"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Otsetee „Vidinad“ lisamiseks veenduge, et seadetes oleks valik „Kuva lukustuskuval vidinad“ lubatud."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Seaded"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Nupp Kuva ekraanisäästja"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rippmenüü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Värskendamine"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Tööprofiil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Lennukirežiim"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Vanemlik järelevalve"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Te ei kuule järgmist äratust kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Kuvatakse mullina vestluste märguannete ülaosas ja profiilipildina lukustuskuval ning katkestab režiimi Mitte segada"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioriteetne"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei toeta vestlusfunktsioone"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Neid märguandeid ei saa muuta."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Kõnemärguandeid ei saa muuta."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Seda märguannete rühma ei saa siin seadistada"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalender"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulaator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Mitte segada"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Helitugevuse nuppude otsetee"</string>
     <string name="battery" msgid="769686279459897127">"Aku"</string>
@@ -1273,7 +1289,7 @@
     <string name="status_before_loading" msgid="1500477307859631381">"Sisu kuvatakse peagi"</string>
     <string name="missed_call" msgid="4228016077700161689">"Vastamata kõne"</string>
     <string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
-    <string name="people_tile_description" msgid="8154966188085545556">"Vaadake hiljutisi sõnumeid, vastamata kõnesid ja olekuvärskendusi"</string>
+    <string name="people_tile_description" msgid="8154966188085545556">"Vaadake hiljutisi sõnumeid, vastamata kõnesid ja olekuvärskendusi."</string>
     <string name="people_tile_title" msgid="6589377493334871272">"Vestlus"</string>
     <string name="paused_by_dnd" msgid="7856941866433556428">"Peatas režiim Mitte segada"</string>
     <string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> saatis sõnumi: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Süsteemirakendused"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitegumtöö"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Jagatud ekraanikuva"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Juurdepääsetavus"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sisend"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Rakenduse otseteed"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Praegune rakendus"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Tegite hiljutiste rakenduste vaatamise liigutuse."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Hiljutiste rakenduste kuvamiseks pühkige puuteplaadil kolme sõrmega üles ja hoidke sõrmi puuteplaadil"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Rakenduste vahetamine"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Väga hea!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Tegite rakenduste vahetamise liigutuse."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Kõigi rakenduste kuvamine"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Vajutage klaviatuuril toiminguklahvi"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 912da58..b8643a2 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Pantaila blokeatuko widgetak"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aplikazio bat widget baten bidez irekitzeko, zeu zarela egiaztatu beharko duzu. Gainera, kontuan izan edonork ikusi ahalko dituela halako widgetak, tableta blokeatuta badago ere. Baliteke widget batzuk pantaila blokeaturako egokiak ez izatea, eta agian ez da segurua haiek bertan gehitzea."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ados"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetak"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Widgetak\" lasterbidea gehitzeko, ziurtatu \"Erakutsi widgetak pantaila blokeatuan\" gaituta dagoela ezarpenetan."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ezarpenak"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Erakutsi pantaila-babeslearen botoia"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"zabaldu menua"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Eguneratzen"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Laneko profila"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Hegaldi modua"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Guraso-murriztapenak"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Ez duzu entzungo hurrengo alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
     <string name="alarm_template" msgid="2234991538018805736">"ordua: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"data: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Elkarrizketen jakinarazpenen goialdean eta profileko argazki gisa agertzen da pantaila blokeatuan, burbuila batean, eta ez molestatzeko modua eteten du"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Lehentasuna"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak ez ditu onartzen elkarrizketetarako eginbideak"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Jakinarazpen horiek ezin dira aldatu."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Deien jakinarazpenak ezin dira aldatu."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Jakinarazpen talde hau ezin da konfiguratu hemen"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulagailua"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ez molestatzeko modua"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Bolumen-botoietarako lasterbidea"</string>
     <string name="battery" msgid="769686279459897127">"Bateria"</string>
@@ -1432,14 +1448,13 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemaren aplikazioak"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Zeregin bat baino gehiago aldi berean exekutatzea"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantaila zatitzea"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Erabilerraztasuna"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sarrera"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Aplikazioetarako lasterbideak"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Oraingo aplikazioa"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erabilerraztasuna"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string>
-    <string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Pertsonalizatu lasterbideak"</string>
+    <string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Pertsonalizatu"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Lasterbidea kendu nahi duzu?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Balio lehenetsia berrezarri nahi duzu?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Lasterbide hau sortzeko, sakatu ekintza-tekla eta beste tekla bat edo gehiago batera"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Osatu duzu azkenaldiko aplikazioak ikusteko keinua."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Azkenaldiko aplikazioak ikusteko, pasatu 3 hatz gora eta eduki sakatuta ukipen-panelean"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Aldatu aplikazioa"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bikain!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ikasi duzu aplikazio batetik bestera aldatzeko keinua."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ikusi aplikazio guztiak"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Sakatu teklatuko ekintza-tekla"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 3612b16..2a3c3c0 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ابزاره‌های صفحه قفل"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزاره، باید هویت خودتان را به‌تأیید برسانید. همچنین، به‌خاطر داشته باشید که همه می‌توانند آن‌ها را مشاهده کنند، حتی وقتی رایانه لوحی‌تان قفل است. برخی‌از ابزاره‌ها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آن‌ها در اینجا ناامن باشد."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجهم"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ابزاره‌ها"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"برای افزودن میان‌بر «ابزاره‌ها»، مطمئن شوید «نمایش ابزاره‌ها در صفحه قفل» در تنظیمات فعال باشد."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"تنظیمات"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"دکمه نمایش دادن محافظ صفحه‌نمایش"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"کاوش کردن حالت متصل"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"در حین شارژ، به ابزاره‌ها و محافظ‌های صفحه‌نمایش دلخواهتان دسترسی داشته باشید."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"بیایید شروع کنیم"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی پایین‌پر"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامه‌ها و داده‌های این جلسه حذف خواهد شد."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"درحال به‌روزرسانی"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"نمایه کاری"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"حالت هواپیما"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"کنترل‌های والدین"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"در ساعت <xliff:g id="WHEN">%1$s</xliff:g>، دیگر صدای زنگ ساعت را نمی‌شنوید"</string>
     <string name="alarm_template" msgid="2234991538018805736">"در <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"در <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"در بالای اعلان‌های مکالمه و به‌صورت عکس نمایه در صفحه قفل نشان داده می‌شود، به‌صورت حبابک ظاهر می‌شود، در حالت «مزاحم نشوید» وقفه ایجاد می‌کند"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"اولویت"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> از ویژگی‌های مکالمه پشتیبانی نمی‌کند"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"این اعلان‌ها قابل اصلاح نیستند."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"این اعلان‌ها قابل‌اصلاح نیستند."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"نمی‌توانید این گروه اعلان‌ها را در اینجا پیکربندی کنید"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"تقویم"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"ماشین‌حساب"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"مزاحم نشوید"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"میان‌بر دکمه‌های صدا"</string>
     <string name="battery" msgid="769686279459897127">"باتری"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"برنامه‌های سیستم"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"چندوظیفگی"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"صفحهٔ دونیمه"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"دسترس‌پذیری"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ورودی"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"میان‌برهای برنامه"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"برنامه فعلی"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"اشاره «مشاهده برنامه‌های اخیر» را تمام کردید"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"برای مشاهده برنامه‌های اخیر، با سه انگشت روی صفحه لمسی تند به‌بالا بکشید و نگه دارید"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"جابه‌جایی بین برنامه‌ها"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"با چهار انگشت روی صفحه لمسی تند به چپ بکشید"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"عالی است!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"اشاره جابه‌جایی بین برنامه‌ها را تکمیل کردید."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"برای جابه‌جا شدن بین برنامه‌ها، با چهار انگشت روی صفحه لمسی تند به چپ بکشید"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"مشاهده همه برنامه‌ها"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"دکمه کنش را روی صفحه لمسی فشار دهید"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"عالی بود!"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 9f1c723..67e0c28 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -537,10 +537,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lukitusnäytön widgetit"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Jos haluat avata sovelluksen käyttämällä widgetiä, sinun täytyy vahvistaa henkilöllisyytesi. Muista myös, että widgetit näkyvät kaikille, vaikka tabletti olisi lukittuna. Jotkin widgetit on ehkä tarkoitettu lukitusnäytölle, ja niiden lisääminen tänne ei välttämättä ole turvallista."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selvä"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetit"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Jos haluat lisätä Widgetit-pikakuvakkeen, varmista, että \"Näytä widgetit lukitusnäytöllä\" on käytössä asetuksissa."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Asetukset"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Näytä näytönsäästäjän painike"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"alasvetovalikko"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
@@ -752,8 +755,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Päivitetään"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Työprofiili"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Lentokonetila"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Lapsilukko"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Et kuule seuraavaa hälytystäsi (<xliff:g id="WHEN">%1$s</xliff:g>)."</string>
     <string name="alarm_template" msgid="2234991538018805736">"kello <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"ajankohtana <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -804,6 +806,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Näkyy keskusteluilmoitusten yläosassa ja profiilikuvana lukitusnäytöllä, näkyy kuplana, keskeyttää Älä häiritse ‑tilan"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Tärkeä"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei tue keskusteluominaisuuksia"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Näitä ilmoituksia ei voi muokata"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Puheluilmoituksia ei voi muokata."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Tätä ilmoitusryhmää ei voi määrittää tässä"</string>
@@ -914,6 +920,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalenteri"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Laskin"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Älä häiritse"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Äänenvoimakkuuspainikkeiden pikanäppäin"</string>
     <string name="battery" msgid="769686279459897127">"Akku"</string>
@@ -1434,8 +1450,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Järjestelmäsovellukset"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitaskaus"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Jaettu näyttö"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Saavutettavuus"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Syöte"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Sovellusten pikakuvakkeet"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Nykyinen sovellus"</string>
@@ -1499,11 +1514,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Olet oppinut Katso viimeisimmät sovellukset ‑eleen."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Näet äskeiset sovellukset, kun pyyhkäiset ylös ja pidät kosketuslevyä painettuna kolmella sormella."</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Vaihda sovellusta"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Hienoa!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Olet oppinut sovelluksenvaihtoeleen."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Näytä kaikki sovellukset"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paina näppäimistön toimintonäppäintä"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 6bcce53..5d0c67b 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de l\'écran de verrouillage"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devrez confirmer votre identité. En outre, gardez à l\'esprit que tout le monde peut voir les widgets, même lorsque votre tablette est verrouillée. Certains widgets n\'ont peut-être pas été conçus pour votre écran de verrouillage, et il pourrait être dangereux de les ajouter ici."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pour ajouter le raccourci « Widgets », assurez-vous que « Afficher les widgets sur l\'écran de verrouillage » est activé dans les paramètres."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Paramètres"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Afficher le bouton de l\'écran de veille"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Explorer le mode Console"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Accéder à vos widgets et écrans de veille préférés pendant le chargement."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Allons-y"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applis et les données de cette session seront supprimées."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mise à jour en cours…"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Contrôles parentaux"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"le <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"S\'affiche dans le haut des notifications de conversation et comme photo de profil à l\'écran de verrouillage, s\'affiche comme bulle, interrompt le mode Ne pas déranger"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritaire"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ne prend pas en charge les fonctionnalités de conversation"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ces notifications ne peuvent pas être modifiées"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Les notifications d\'appel ne peuvent pas être modifiées."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ce groupe de notifications ne peut pas être configuré ici"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Agenda"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculatrice"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ne pas déranger"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Raccourci des boutons de volume"</string>
     <string name="battery" msgid="769686279459897127">"Pile"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Applis système"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Écran divisé"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accessibilité"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis des applis"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Vous avez effectué le geste pour afficher les applis récentes."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Pour afficher vos applis récentes, balayez votre pavé tactile vers le haut avec trois doigts et maintenez-les en place"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Changer d\'appli"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Balayez votre pavé tactile vers la droite avec quatre doigts"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bon travail!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Vous avez terminé le geste de changement d\'appli."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Balayez votre pavé tactile vers la droite avec quatre doigts pour changer d\'appli"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Afficher toutes les applis"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Félicitations!"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 3362826..3899576 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets pour l\'écran de verrouillage"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devez confirmer qu\'il s\'agit bien de vous. N\'oubliez pas non plus que tout le monde peut voir vos widgets, même lorsque votre tablette est verrouillée. Certains d\'entre eux n\'ont pas été conçus pour l\'écran de verrouillage et les ajouter à cet endroit peut s\'avérer dangereux."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pour ajouter le raccourci \"Widgets\", assurez-vous que l\'option \"Afficher les widgets sur l\'écran de verrouillage\" est activée dans les paramètres."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Paramètres"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Afficher le bouton \"Économiseur d\'écran\""</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Découvrir le mode Hub"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Accédez à vos widgets et économiseurs d\'écran préférés lorsque l\'appareil est en charge."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"C\'est parti"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mise à jour"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Contrôle parental"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme <xliff:g id="WHEN">%1$s</xliff:g>."</string>
     <string name="alarm_template" msgid="2234991538018805736">"à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"le <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,8 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"S\'affiche en haut des notifications de conversation et en tant que photo de profil sur l\'écran de verrouillage, apparaît sous forme de bulle, interrompt le mode Ne pas déranger"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritaire"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas compatible avec les fonctionnalités de conversation"</string>
+    <string name="notification_inline_dismiss" msgid="88423586921134258">"Ignorer"</string>
+    <string name="notification_inline_disable_promotion" msgid="6880961831026048166">"Ne plus afficher"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Impossible de modifier ces notifications."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Impossible de modifier les notifications d\'appel."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Vous ne pouvez pas configurer ce groupe de notifications ici"</string>
@@ -912,6 +913,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Agenda"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculatrice"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ne pas déranger"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Raccourci des boutons de volume"</string>
     <string name="battery" msgid="769686279459897127">"Batterie"</string>
@@ -1432,8 +1443,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Applis système"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Écran partagé"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accessibilité"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Saisie"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis d\'application"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
@@ -1497,12 +1507,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Vous avez appris le geste pour afficher les applis récentes"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Pour afficher les applis récentes, balayez le pavé tactile vers le haut avec trois doigts et maintenez la position"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Passer d\'une application à l\'autre"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Balayez vers la droite avec quatre doigts sur le pavé tactile"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bravo !"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Vous avez appris le geste pour passer d\'une appli à l\'autre."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Balayez vers la droite avec quatre doigts sur le pavé tactile pour passer d\'une appli à une autre"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Afficher toutes les applications"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bravo !"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 60fe0b0..5632327 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da pantalla de bloqueo"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir unha aplicación mediante un widget, tes que verificar a túa identidade. Ten en conta que pode velos calquera persoa, mesmo coa tableta bloqueada. Pode ser que algúns widgets non estean pensados para a túa pantalla de bloqueo, polo que talvez non sexa seguro engadilos aquí."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para engadir o atallo Widgets, vai a Configuración e comproba que está activada a opción Mostrar widgets na pantalla de bloqueo."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configuración"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botón para mostrar o protector de pantalla"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Actualizando"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de traballo"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Controis parentais"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Non escoitarás a alarma seguinte <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"ás <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Móstrase na parte superior das notificacións das conversas e como imaxe do perfil na pantalla de bloqueo, aparece como unha burbulla e interrompe o modo Non molestar"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> non admite funcións de conversa"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Estas notificacións non se poden modificar."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"As notificacións de chamadas non se poden modificar."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Aquí non se pode configurar este grupo de notificacións"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Non molestar"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Atallo dos botóns de volume"</string>
     <string name="battery" msgid="769686279459897127">"Batería"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicacións do sistema"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefa"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accesibilidade"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atallos de aplicacións"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación actual"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaches o titorial do xesto de consultar aplicacións recentes."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Para ver as aplicacións recentes, pasa tres dedos cara arriba e mantenos premidos no panel táctil"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Cambiar de aplicación"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bravo!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Completaches o xesto para cambiar de aplicación."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas as aplicacións"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Preme a tecla de acción do teclado"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index f9c5b81..5c41423 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"લૉક સ્ક્રીન વિજેટ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"વિજેટનો ઉપયોગ કરીને ઍપ ખોલવા માટે, તમારે એ ચકાસણી કરવાની જરૂર રહેશે કે આ તમે જ છો. તે ઉપરાંત, ધ્યાનમાં રાખો કે તમારું ટૅબ્લેટ લૉક કરેલું હોય તો પણ કોઈપણ વ્યક્તિ તેમને જોઈ શકે છે. અમુક વિજેટ કદાચ તમારી લૉક સ્ક્રીન માટે બનાવવામાં આવ્યા ન હોઈ શકે છે અને તેમને અહીં ઉમેરવાનું અસલામત હોઈ શકે છે."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"સમજાઈ ગયું"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"વિજેટ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"વિજેટ\"નો શૉર્ટકટ ઉમેરવા માટે, ખાતરી કરો કે સેટિંગમાં \"લૉક સ્ક્રીન પર વિજેટ બતાવો\" સુવિધા ચાલુ કરેલી છે."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"સેટિંગ"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"સ્ક્રીનસેવર બટન બતાવો"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"વપરાશકર્તા સ્વિચ કરો"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"પુલડાઉન મેનૂ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"અપડેટ કરી રહ્યાં છીએ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ઑફિસની પ્રોફાઇલ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"એરપ્લેન મોડ"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"માતાપિતાના યોગ્ય નિયંત્રણો"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"તમે <xliff:g id="WHEN">%1$s</xliff:g> એ તમારો આગલો એલાર્મ સાંભળશો નહીં"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> વાગ્યે"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> એ"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, બબલ તરીકે દેખાય છે, ખલેલ પાડશો નહીં મોડમાં વિક્ષેપ ઊભો કરે છે"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"પ્રાધાન્યતા"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> વાતચીતની સુવિધાઓને સપોર્ટ આપતી નથી"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"આ નોટિફિકેશનમાં કોઈ ફેરફાર થઈ શકશે નહીં."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"કૉલના નોટિફિકેશનમાં કોઈ ફેરફાર કરી શકાતો નથી."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"નોટિફિકેશનના આ ગ્રૂપની ગોઠવણી અહીં કરી શકાશે નહીં"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"કેલ્ક્યુલેટર"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ખલેલ પાડશો નહીં"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"વૉલ્યૂમ બટન્સ શૉર્ટકટ"</string>
     <string name="battery" msgid="769686279459897127">"બૅટરી"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"સિસ્ટમ ઍપ"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"એકથી વધુ કાર્યો કરવા"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"સ્ક્રીનને વિભાજિત કરો"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ઍક્સેસિબિલિટી"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ઇનપુટ"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ઍપ શૉર્ટકટ"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"હાલની ઍપ"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"તમે \'તાજેતરની ઍપ જુઓ\' સંકેત પૂર્ણ કર્યો."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"તાજેતરની ઍપ જોવા માટે, તમારા ટચપૅડ પર ત્રણ આંગળી વડે ઉપરની તરફ સ્વાઇપ કરો અને દબાવી રાખો"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ઍપ સ્વિચ કરો"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ખૂબ સરસ કામ!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"તમે ઍપ સ્વિચ કરવાનો સંકેત પૂર્ણ કર્યો છે."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"બધી ઍપ જુઓ"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"તમારા કીબોર્ડ પરની ઍક્શન કી દબાવો"</string>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index 8c8d5f6..17b47cc 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -193,7 +193,7 @@
   </string-array>
   <string-array name="tile_states_notes">
     <item msgid="5894333929299989301">"અનુપલબ્ધ"</item>
-    <item msgid="6419996398343291862">"બંધ"</item>
+    <item msgid="6419996398343291862">"બંધ છે"</item>
     <item msgid="5908720590832378783">"ચાલુ"</item>
   </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index efb080a..221327c 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्क्रीन विजेट"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि आपके टैबलेट के लॉक होने पर भी, कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट, लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ठीक है"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेट"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"विजेट\" शॉर्टकट जोड़ने के लिए, पक्का करें कि सेटिंग में \"लॉक स्क्रीन पर विजेट दिखाएं\" चालू हो."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"सेटिंग"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"स्क्रीन सेवर दिखाने का बटन"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सेशन के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"अपडेट हो रहा है"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"वर्क प्रोफ़ाइल"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"फ़्लाइट मोड"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"माता-पिता के कंट्रोल"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"आपको <xliff:g id="WHEN">%1$s</xliff:g> पर अपना अगला अलार्म नहीं सुनाई देगा"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> बजे"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> पर"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"यह कई तरीकों से दिखती है, जैसे कि बातचीत वाली सूचनाओं में सबसे ऊपर, बबल के तौर पर, और लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो के तौर पर. साथ ही, यह \'परेशान न करें\' मोड को बायपास कर सकती है"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर बातचीत की सुविधाएं काम नहीं करतीं"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"ये सूचनाएं नहीं बदली जा सकती हैं."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"कॉल से जुड़ी सूचनाओं को ब्लॉक नहीं किया जा सकता."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"सूचनाओं के इस समूह को यहां कॉन्फ़िगर नहीं किया जा सकता"</string>
@@ -878,8 +884,8 @@
     <string name="group_system_go_back" msgid="2730322046244918816">"वापस जाने के लिए"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"होम स्क्रीन पर जाने के लिए"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखने के लिए"</string>
-    <string name="group_system_cycle_forward" msgid="5478663965957647805">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन के अगले पेज पर जाने के लिए"</string>
-    <string name="group_system_cycle_back" msgid="8194102916946802902">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन के पिछले पेज पर जाने के लिए"</string>
+    <string name="group_system_cycle_forward" msgid="5478663965957647805">"हाल में इस्तेमाल किए गए ऐप्लिकेशन पर, सीधे क्रम में वापस जाने के लिए"</string>
+    <string name="group_system_cycle_back" msgid="8194102916946802902">"हाल में इस्तेमाल किए गए ऐप्लिकेशन पर, उलटे क्रम में वापस जाने के लिए"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"ऐप्लिकेशन की सूची खोलने के लिए"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"सेटिंग खोलने के लिए"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"सहायक ऐप्लिकेशन खोलने के लिए"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"कैलकुलेटर"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"मैप"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"परेशान न करें"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"वॉल्यूम बटन का शॉर्टकट"</string>
     <string name="battery" msgid="769686279459897127">"बैटरी"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टम के ऐप्लिकेशन"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टीटास्किंग (एक साथ कई काम करना)"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रीन"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"सुलभता"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ऐप शॉर्टकट"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"मौजूदा ऐप्लिकेशन"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"अब आपको हाथ के जेस्चर का इस्तेमाल करके, हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखने का तरीका पता चल गया है."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखने के लिए, अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें और दबाकर रखें"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ऐप्लिकेशन के बीच स्विच करें"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"बहुत बढ़िया!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"आपने जान लिया है कि हाथ का जेस्चर इस्तेमाल करके ऐप्लिकेशन के बीच स्विच कैसे करें."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"सभी ऐप्लिकेशन देखें"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"अपने कीबोर्ड पर ऐक्शन बटन दबाएं"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 5078bff..60b39a4 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeti na zaključanom zaslonu"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju pomoću widgeta, trebate potvrditi da ste to vi. Također napominjemo da ih svatko može vidjeti, čak i ako je vaš tablet zaključan. Neki widgeti možda nisu namijenjeni za zaključani zaslon, pa ih možda nije sigurno dodati ovdje."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Shvaćam"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgeti"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da biste dodali prečac Widgeti, provjerite je li u postavkama omogućena opcija Prikaži widgete na zaključanom zaslonu."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Postavke"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Prikaži gumb čuvara zaslona"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Istraži način Huba"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Pristupite omiljenim widgetima i čuvarima zaslona tijekom punjenja."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Započnimo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući izbornik"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Izbrisat će se sve aplikacije i podaci u ovoj sesiji."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ažuriranje"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u zrakoplovu"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Roditeljski nadzor"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Prikazuje se pri vrhu obavijesti razgovora i kao profilna slika na zaključanom zaslonu, izgleda kao oblačić, prekida Ne uznemiravaj"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritetno"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ne podržava značajke razgovora"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Te se obavijesti ne mogu izmijeniti."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Obavijesti o pozivima ne mogu se izmijeniti."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ta se grupa obavijesti ne može konfigurirati ovdje"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Karte"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ne uznemiravaj"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Prečac tipki za glasnoću"</string>
     <string name="battery" msgid="769686279459897127">"Baterija"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacije sustava"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podijeljeni zaslon"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Pristupačnost"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečaci aplikacija"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutačna aplikacija"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Napravili ste pokret za prikaz nedavno korištenih aplikacija."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Za prikaz nedavnih aplikacija prijeđite trima prstima prema gore na dodirnoj podlozi i zadržite pritisak"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Promjena aplikacije"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Prijeđite udesno četirima prstima na dodirnoj podlozi"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Sjajno!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Izvršili ste pokret za promjenu aplikacije."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Prijeđite udesno četirima prstima na dodirnoj podlozi za prebacivanje između aplikacija"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Prikaži sve aplikacije"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku za radnju na tipkovnici"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Izvrsno!"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index cce2732..568c872 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"A lezárási képernyő moduljai"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ha modul használatával szeretne megnyitni egy alkalmazást, igazolnia kell a személyazonosságát. Ne felejtse továbbá, hogy bárki megtekintheti a modulokat, még akkor is, amikor zárolva van a táblagép. Előfordulhat, hogy bizonyos modulokat nem a lezárási képernyőn való használatra terveztek, ezért nem biztonságos a hozzáadásuk."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Értem"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Modulok"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"A „Modulok” gyorsparancs hozzáadásához gondoskodjon arról, hogy a „Modulok megjelenítése a lezárási képernyőn” beállítás legyen engedélyezve a beállításokban."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Beállítások"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Képernyőkímélő gomb megjelenítése"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"A Hub mód felfedezése"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Töltés közben hozzáférhet kedvenc moduljaihoz és képernyőkímélőihez."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Kezdés"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"lehúzható menü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Frissítés…"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Munkahelyi profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Repülős üzemmód"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Szülői felügyelet"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nem fogja hallani az ébresztést ekkor: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"ekkor: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"ezen a napon: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"A beszélgetésekre vonatkozó értesítések tetején, lebegő buborékként látható, megjeleníti a profilképet a lezárási képernyőn, és megszakítja a Ne zavarjanak funkciót"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritás"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> nem támogatja a beszélgetési funkciókat"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ezeket az értesítéseket nem lehet módosítani."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"A hívásértesítéseket nem lehet módosítani."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Az értesítések jelen csoportját itt nem lehet beállítani"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Naptár"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Számológép"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Térkép"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ne zavarjanak"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"A hangerőgombok gyorsbillentyűk"</string>
     <string name="battery" msgid="769686279459897127">"Akkumulátor"</string>
@@ -1432,14 +1445,13 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Rendszeralkalmazások"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Osztott képernyő"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Kisegítő lehetőségek"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Bevitel"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Alkalmazásikonok"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Jelenlegi alkalmazás"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kisegítő lehetőségek"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string>
-    <string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Gyorsparancsok személyre szabása"</string>
+    <string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Billentyűparancsok"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Eltávolítja a billentyűparancsot?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Visszaállítja az alapértelmezett beállításokat?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"A billentyűparancs létrehozásához nyomja le egyszerre a műveletbillentyűt és egy vagy több másik billentyűt"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Teljesítette a legutóbbi alkalmazások megtekintésének kézmozdulatát."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"A legutóbbi appokért csúsztasson gyorsan három ujjal felfelé az érintőpadon, és tartsa lenyomva ujjait."</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Váltás az alkalmazások között"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Csúsztasson gyorsan négy ujjal jobbra az érintőpadon"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Kiváló!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Teljesítette az appváltó gesztust."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Az alkalmazások közötti váltáshoz csúsztasson gyorsan négy ujjal jobbra az érintőpadon"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Összes alkalmazás megtekintése"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nyomja meg a műveletbillentyűt az érintőpadon."</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Szép munka!"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 10cf670..60b445f 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Կողպէկրանի վիջեթներ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Վիջեթի միջոցով հավելված բացելու համար դուք պետք է հաստատեք ձեր ինքնությունը։ Նաև նկատի ունեցեք, որ ցանկացած ոք կարող է դիտել վիջեթները, նույնիսկ երբ ձեր պլանշետը կողպված է։ Որոշ վիջեթներ կարող են նախատեսված չլինել ձեր կողպէկրանի համար, և այստեղ դրանց ավելացնելը կարող է վտանգավոր լինել։"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Եղավ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Վիջեթներ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"«Վիջեթներ» դյուրանցումն ավելացնելու համար համոզվեք, որ «Ցույց տալ վիջեթները կողպէկրանին» պարամետրը միացված է կարգավորումներում։"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Կարգավորումներ"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"«Ցույց տալ էկրանապահը» կոճակ"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Անջատել օգտվողին"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"իջնող ընտրացանկ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն:"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Թարմացում"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Android for Work-ի պրոֆիլ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Ավիառեժիմ"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Ծնողական վերահսկողություն"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Ժամը <xliff:g id="WHEN">%1$s</xliff:g>-ի զարթուցիչը չի զանգի"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>-ին"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-ին"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Ցուցադրվում է զրույցների ծանուցումների վերևում, ինչպես նաև կողպէկրանին որպես պրոֆիլի նկար, հայտնվում է ամպիկի տեսքով, ընդհատում է «Չանհանգստացնել» ռեժիմը"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Կարևոր"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը զրույցի գործառույթներ չի աջակցում"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Այս ծանուցումները չեն կարող փոփոխվել:"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Զանգերի մասին ծանուցումները հնարավոր չէ փոփոխել։"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ծանուցումների տվյալ խումբը հնարավոր չէ կարգավորել այստեղ"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Օրացույց"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Հաշվիչ"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Քարտեզներ"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Չանհանգստացնել"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Ձայնի կոճակների դյուրանցում"</string>
     <string name="battery" msgid="769686279459897127">"Մարտկոց"</string>
@@ -1497,11 +1513,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Դուք կատարեցիք վերջին օգտագործված հավելվածների դիտման ժեստը։"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Վերջին հավելվածները տեսնելու համար երեք մատը սահեցրեք վերև և սեղմած պահեք հպահարթակին"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Անցում մեկ հավելվածից մյուսին"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Կեցցե՛ք։"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Դուք սովորեցիք ուրիշ հավելված անցնելու ժեստը։"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ինչպես դիտել բոլոր հավելվածները"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
@@ -1511,7 +1527,7 @@
     <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Ուղեցույցի անիմացիա․ սեղմեք՝ նվագարկումը դադարեցնելու/վերսկսելու համար։"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string>
-    <string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string>
+    <string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարում"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Տան կառավարման տարրերը դարձրեք էկրանապահ"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Հետարկել"</string>
     <string name="back_edu_toast_content" msgid="4530314597378982956">"Հետ գնալու համար երեք մատը հպահարթակի վրա սահեցրեք ձախ կամ աջ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index a5f0199..0a6baca 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget layar kunci"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka aplikasi menggunakan widget, Anda perlu memverifikasi diri Anda. Selain itu, harap ingat bahwa siapa saja dapat melihatnya, bahkan saat tablet Anda terkunci. Beberapa widget mungkin tidak dirancang untuk layar kunci Anda dan mungkin tidak aman untuk ditambahkan di sini."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Oke"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Untuk menambahkan pintasan \"Widget\", pastikan \"Tampilkan widget di layar kunci\" diaktifkan di setelan."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Setelan"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Tampilkan tombol screensaver"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Beralih pengguna"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pulldown"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data dalam sesi ini akan dihapus."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Update berlangsung"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mode pesawat"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Kontrol orang tua"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar alarm berikutnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"pukul <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Muncul teratas di notifikasi percakapan dan sebagai foto profil di layar kunci, ditampilkan sebagai balon, menimpa mode Jangan Ganggu"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritas"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak mendukung fitur percakapan"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Notifikasi ini tidak dapat diubah."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Notifikasi panggilan tidak dapat diubah."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Grup notifikasi ini tidak dapat dikonfigurasi di sini"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalender"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Jangan Ganggu"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Pintasan tombol volume"</string>
     <string name="battery" msgid="769686279459897127">"Baterai"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikasi sistem"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Layar terpisah"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Aksesibilitas"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan aplikasi"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikasi Saat Ini"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Anda telah menyelesaikan gestur untuk melihat aplikasi terbaru."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Untuk melihat aplikasi terbaru, geser ke atas dan tahan menggunakan tiga jari di touchpad"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Beralih aplikasi"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bagus!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Anda telah menyelesaikan gestur beralih aplikasi."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Lihat semua aplikasi"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan tombol tindakan di keyboard"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 576a227..46c8452 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Græjur fyrir lásskjá"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Þú þarft að staðfesta að þetta sért þú til að geta opnað forrit með græju. Hafðu einnig í huga að hver sem er getur skoðað þær, jafnvel þótt spjaldtölvan sé læst. Sumar græjur eru hugsanlega ekki ætlaðar fyrir lásskjá og því gæti verið óöruggt að bæta þeim við hér."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ég skil"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Græjur"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Gakktu úr skugga um að kveikt sé á „Sýna græjur á lásskjá“ til að geta bætt flýtileiðinni „Græjur“ við."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Stillingar"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Hnappurinn „Sýna skjávara“"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Fellivalmynd"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Uppfærir"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Vinnusnið"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flugstilling"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Barnalæsing"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Ekki mun heyrast í vekjaranum <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Birtist efst í samtalstilkynningum og sem prófílmynd á lásskjánum. Birtist sem blaðra sem truflar „Ónáðið ekki“"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Forgangur"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> styður ekki samtalseiginleika"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ekki er hægt að breyta þessum tilkynningum."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Ekki er hægt að breyta tilkynningum um símtöl."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ekki er hægt að stilla þessar tilkynningar hér"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Dagatal"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Reiknivél"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Kort"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ónáðið ekki"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Flýtihnappar fyrir hljóðstyrk"</string>
     <string name="battery" msgid="769686279459897127">"Rafhlaða"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Kerfisforrit"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Fjölvinnsla"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Skjáskipting"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Aðgengi"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Innsláttur"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Flýtileiðir forrita"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Núverandi forrit"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Þú framkvæmdir bendinguna til að sjá nýleg forrit."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Strjúktu upp og haltu þremur fingrum inni á snertifletinum til að sjá nýleg forrit"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Að skipta á milli forrita"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Vel gert!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Þú framkvæmdir bendinguna til að skipta á milli forrita."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Sjá öll forrit"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Ýttu á aðgerðalykilinn á lyklaborðinu"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 9b847c4..30ad41f6 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget della schermata di blocco"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per aprire un\'app utilizzando un widget, dovrai verificare la tua identità. Inoltre tieni presente che chiunque può vederlo, anche quando il tablet è bloccato. Alcuni widget potrebbero non essere stati progettati per la schermata di blocco e potrebbe non essere sicuro aggiungerli qui."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Per aggiungere la scorciatoia \"Widget\", assicurati che l\'opzione \"Mostra widget sulla schermata di blocco\" sia abilitata nelle impostazioni."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Impostazioni"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Pulsante Mostra salvaschermo"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu a discesa"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Aggiornamento in corso…"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profilo di lavoro"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modalità aereo"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Controllo genitori"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Non sentirai la tua prossima sveglia <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"alle <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Appare in cima alle notifiche delle conversazioni, come immagine del profilo nella schermata di blocco e sotto forma di bolla, inoltre interrompe la modalità Non disturbare"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Priorità"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> non supporta le funzionalità delle conversazioni"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Impossibile modificare queste notifiche."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Impossibile modificare gli avvisi di chiamata."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Qui non è possibile configurare questo gruppo di notifiche"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendario"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calcolatrice"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Non disturbare"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Pulsanti del volume come scorciatoia"</string>
     <string name="battery" msgid="769686279459897127">"Batteria"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"App di sistema"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Schermo diviso"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accessibilità"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Scorciatoie app"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App corrente"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Hai completato il gesto Visualizza app recenti."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Per visualizzare le app recenti, scorri verso l\'alto e tieni premuto con tre dita sul touchpad"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Cambia app"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Ottimo lavoro."</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Hai completato il gesto Cambia app."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Visualizza tutte le app"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Premi il tasto azione sulla tastiera"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index afa1af5..df56148 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -225,7 +225,7 @@
     <string name="biometric_re_enroll_notification_content" msgid="8685925877186288180">"הפעולה הזו נדרשת כדי לשפר את האבטחה והביצועים"</string>
     <string name="fingerprint_re_enroll_notification_title" msgid="4539432429683916604">"הגדרה חוזרת של \'ביטול הנעילה בטביעת אצבע\'"</string>
     <string name="fingerprint_re_enroll_notification_name" msgid="630798657797645704">"פתיחה בטביעת אצבע"</string>
-    <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"הגדרת \'ביטול הנעילה בטביעת אצבע\'"</string>
+    <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"הגדרת \"פתיחה בטביעת אצבע\""</string>
     <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"כדי להגדיר שוב את התכונה \'ביטול הנעילה בטביעת אצבע\', עליך למחוק את התבניות והמודלים הנוכחיים של טביעת האצבע.\n\nאחרי המחיקה יהיה צורך להגדיר שוב את \'ביטול הנעילה בטביעת אצבע\' כדי להשתמש בטביעת האצבע לביטול הנעילה של הטלפון ולאמת את זהותך."</string>
     <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"כדי להגדיר שוב את התכונה \'ביטול הנעילה בטביעת אצבע\', עליך למחוק את התבניות והמודל הנוכחיים של טביעת האצבע.\n\nאחרי המחיקה יהיה צורך להגדיר שוב את \'ביטול הנעילה בטביעת אצבע\' כדי להשתמש בטביעת האצבע לביטול הנעילה של הטלפון ולאמת את זהותך."</string>
     <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"לא ניתן להגדיר ביטול נעילה בטביעת אצבע. יש לעבור להגדרות כדי לנסות שוב."</string>
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ווידג\'טים במסך הנעילה"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"כדי לפתוח אפליקציה באמצעות ווידג\'ט, עליך לאמת את זהותך. בנוסף, כדאי לזכור שכל אחד יכול לראות את הווידג\'טים גם כשהטאבלט שלך נעול. יכול להיות שחלק מהווידג\'טים לא נועדו למסך הנעילה ושלא בטוח להוסיף אותם לכאן."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"הבנתי"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ווידג\'טים"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"כדי להוסיף את קיצור הדרך \"ווידג\'טים\", צריך לוודא שהאפשרות \"ווידג\'טים במסך הנעילה\" מופעלת בהגדרות."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"הגדרות"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"כפתור להצגת שומר המסך"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"החלפת משתמש"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"תפריט במשיכה למטה"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בסשן הזה יימחקו."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"מתבצע עדכון"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"פרופיל עבודה"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"מצב טיסה"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"אמצעי בקרת הורים"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"לא ניתן יהיה לשמוע את השעון המעורר הבא שלך <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"בשעה <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"ב-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"מוצגת בחלק העליון של קטע התראות השיחה וכתמונת פרופיל במסך הנעילה, מופיעה בבועה צפה ומפריעה במצב \'נא לא להפריע\'"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"בעדיפות גבוהה"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> לא תומכת בתכונות השיחה"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"לא ניתן לשנות את ההתראות האלה."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"לא ניתן לשנות את התראות השיחה."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"לא ניתן להגדיר כאן את קבוצת ההתראות הזו"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"יומן"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"מחשבון"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"מפות"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"נא לא להפריע"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"קיצור דרך לכפתורי עוצמת קול"</string>
     <string name="battery" msgid="769686279459897127">"סוללה"</string>
@@ -1428,12 +1444,11 @@
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"בשימוש על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"נעשה שימוש לאחרונה על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="shortcut_helper_category_system" msgid="462110876978937359">"מערכת"</string>
-    <string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"הגדרות המערכת"</string>
+    <string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"שליטה במערכת"</string>
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"אפליקציות מערכת"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ריבוי משימות"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"מסך מפוצל"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"נגישות"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"קלט"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"מקשי קיצור לאפליקציות"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"האפליקציה הנוכחית"</string>
@@ -1478,8 +1493,7 @@
     <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"חזרה"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"חזרה לדף הבית"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"הצגת האפליקציות האחרונות"</string>
-    <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
-    <skip />
+    <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"מעבר בין אפליקציות"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"סיום"</string>
     <string name="gesture_error_title" msgid="469064941635578511">"צריך לנסות שוב."</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"חזרה"</string>
@@ -1497,15 +1511,12 @@
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"מעולה!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"סיימת לתרגל את התנועה להצגת האפליקציות האחרונות."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"כדי לראות את האפליקציות האחרונות, צריך להחליק למעלה וללחוץ לחיצה ארוכה עם שלוש אצבעות על לוח המגע"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
+    <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"מעבר בין אפליקציות"</string>
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
-    <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
-    <skip />
-    <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
-    <skip />
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"מעולה!"</string>
+    <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"השלמת את תנועת המעבר בין האפליקציות."</string>
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"צפייה בכל האפליקציות"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"צריך להקיש על מקש הפעולה במקלדת"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 87c6aed..d588590 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ロック画面ウィジェット"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ウィジェットを使用してアプリを起動するには、本人確認が必要です。タブレットがロックされた状態でも他のユーザーにウィジェットが表示されますので、注意してください。一部のウィジェットについてはロック画面での使用を想定していないため、ロック画面への追加は危険な場合があります。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ウィジェット"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"[ウィジェット] ショートカットを追加するには、設定で [ロック画面でのウィジェットの表示] が有効になっていることを確認してください。"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"設定"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"スクリーンセーバー表示ボタン"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"ハブモードの詳細を見る"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"充電中にお気に入りのウィジェットやスクリーン セーバーにアクセスできます。"</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"使ってみる"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"プルダウン メニュー"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"更新しています"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"仕事用プロファイル"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"機内モード"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"保護者による使用制限"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"次回のアラーム(<xliff:g id="WHEN">%1$s</xliff:g>)は鳴りません"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"会話通知の一番上に表示されると同時に、ロック画面にプロフィール写真として表示されるほか、バブルとして表示され、サイレント モードが中断されます"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"優先"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>は会話機能に対応していません"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"これらの通知は変更できません。"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"着信通知は変更できません。"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"このグループの通知はここでは設定できません"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"カレンダー"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"電卓"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"マップ"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"サイレント モード"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"音量ボタンのショートカット"</string>
     <string name="battery" msgid="769686279459897127">"バッテリー"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"システムアプリ"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"マルチタスク"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割画面"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ユーザー補助"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"入力"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"アプリのショートカット"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"現在のアプリ"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"「最近使ったアプリを表示する」ジェスチャーを学習しました。"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"最近使ったアプリを表示するには、3 本の指でタッチパッドを上にスワイプして長押しします"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"アプリの切り替え"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"タッチパッドを 4 本の指で右にスワイプします"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"よくできました!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"アプリを切り替えるジェスチャーを学習しました。"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"アプリを切り替えるには、タッチパッドを 4 本の指で右にスワイプします"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"すべてのアプリを表示"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"キーボードのアクションキーを押します"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"完了です!"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 31f486f..1a2d2be 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"დაბლოკილი ეკრანის ვიჯეტები"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"უნდა დაადასტუროთ თქვენი ვინაობა, რათა გახსნათ აპი ვიჯეტის გამოყენებით. გაითვალისწინეთ, რომ ნებისმიერს შეუძლია მათი ნახვა, მაშინაც კი, როცა ტაბლეტი დაბლოკილია. ზოგი ვიჯეტი შეიძლება არ იყოს გათვლილი თქვენი დაბლოკილი ეკრანისთვის და მათი აქ დამატება შეიძლება სახიფათო იყოს."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"გასაგებია"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ვიჯეტები"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"„ვიჯეტების“ მალსახმობის დასამატებლად დარწმუნდით, რომ პარამეტრებში ჩართულია „დაბლოკილ ეკრანზე ვიჯეტების ჩვენება“."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"პარამეტრები"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ეკრანმზოგის ღილაკის ჩვენება"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"ჰაბის რეჟიმის დათვალიერება"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"დატენის დროს შეგიძლიათ თქვენს რჩეულ ვიჯეტებზე და ეკრანმზოგებზე წვდომა."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"დავიწყოთ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომხმარებლის გადართვა"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ჩამოშლადი მენიუ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"მიმდინარეობს განახლება"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"სამსახურის პროფილი"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"თვითმფრინავის რეჟიმი"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"მშობელთა კონტროლი"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"ვერ გაიგონებთ მომდევნო მაღვიძარას <xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"გამოჩნდება საუბრის შეტყობინებების თავში და პროფილის სურათის სახით ჩაკეტილ ეკრანზე, ჩნდება ბუშტის სახით, წყვეტს ფუნქციას „არ შემაწუხოთ“"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"პრიორიტეტი"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ს არ აქვს მიმოწერის ფუნქციების მხარდაჭერა"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"ამ შეტყობინებების შეცვლა შეუძლებელია."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ზარის შეტყობინებების შეცვლა შეუძლებელია."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"შეტყობინებების ამ ჯგუფის კონფიგურირება აქ შეუძლებელია"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"კალენდარი"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"კალკულატორი"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"არ შემაწუხოთ"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ხმის ღილაკების მალსახმობი"</string>
     <string name="battery" msgid="769686279459897127">"ბატარეა"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"სისტემის აპები"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"მრავალამოცანიანი რეჟიმი"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ეკრანის გაყოფა"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"მარტივი წვდომა"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"შეყვანა"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"აპის მალსახმობები"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"მიმდინარე აპი"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"თქვენ დაასრულეთ ბოლო აპების ხედის ჟესტი."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ბოლო აპების სანახავად თქვენს სენსორულ პანელზე სამი თითით გადაფურცლეთ ზევით და ხანგრძლივად დააჭირეთ"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"აპების გადართვა"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"თქვენს სენსორულ პანელზე ოთხი თითით გადაფურცლეთ მარჯვნივ"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"შესანიშნავია!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"თქვენ შეასრულეთ აპების გადართვის ჟესტი."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"აპების გადასართავად თქვენს სენსორულ პანელზე ოთხი თითით გადაფურცლეთ მარჯვნივ"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"ყველა აპის ნახვა"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"დააჭირეთ მოქმედების კლავიშს თქვენს კლავიატურაზე"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ყოჩაღ!"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 0b6c7b9..54fb72a7 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Құлып экранының виджеттері"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Қолданбаны виджет көмегімен ашу үшін жеке басыңызды растауыңыз керек. Сондай-ақ басқалар оларды планшетіңіз құлыптаулы кезде де көре алатынын ескеріңіз. Кейбір виджеттер құлып экранына арналмаған болады, сондықтан оларды мұнда қосу қауіпсіз болмауы мүмкін."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түсінікті"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеттер"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджеттер\" таңбашасын қосу үшін параметрлерде \"Виджеттерді құлыптаулы экранда көрсету\" опциясының қосулы екенін тексеріңіз."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Параметрлер"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Скринсейвер түймесін көрсету"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Пайдаланушыны ауыстыру"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ашылмалы мәзір"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданба мен дерек жойылады."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Жаңартылып жатыр"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Жұмыс профилі"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Ұшақ режимі"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Ата-ана бақылауы"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Келесі <xliff:g id="WHEN">%1$s</xliff:g> дабылыңызды есітпейсіз"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Әңгіме туралы хабарландырулардың жоғарғы жағында тұрады және құлыптаулы экранда профиль суреті болып көрсетіледі, қалқыма хабар түрінде шығады, Мазаламау режимін тоқтатады."</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Маңызды"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> әңгіме функцияларын қолдамайды."</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Бұл хабарландыруларды өзгерту мүмкін емес."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Қоңырау туралы хабарландыруларды өзгерту мүмкін емес."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Мұндай хабарландырулар бұл жерде конфигурацияланбайды."</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Күнтізбе"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Мазаламау"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Дыбыс деңгейі түймелерінің төте жолы"</string>
     <string name="battery" msgid="769686279459897127">"Батарея"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Жүйелік қолданбалар"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мультитаскинг"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Экранды бөлу"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Арнайы мүмкіндіктер"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Енгізу"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Қолданба таңбашалары"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Қолданыстағы қолданба"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Соңғы қолданбаларды көру қимылын орындадыңыз."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Соңғы қолданбаларды көру үшін сенсорлық тақтада үш саусақпен жоғары сырғытып, ұстап тұрыңыз."</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Қолданба ауыстыру"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Жарайсыз!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Қолданба ауыстыру қимылын аяқтадыңыз."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Барлық қолданбаны көру"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Пернетақтадағы әрекет пернесін басыңыз."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index a2f5b01..e827fc7 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ធាតុ​ក្រាហ្វិកលើអេក្រង់ចាក់សោ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ដើម្បីបើកកម្មវិធីដោយប្រើធាតុ​ក្រាហ្វិក អ្នកនឹងត្រូវផ្ទៀងផ្ទាត់ថាជាអ្នក។ ទន្ទឹមនឹងនេះ សូមចងចាំថា នរណាក៏អាចមើលធាតុក្រាហ្វិកបាន សូម្បីពេលថេប្លេតរបស់អ្នកជាប់សោក៏ដោយ។ ធាតុ​ក្រាហ្វិកមួយចំនួនប្រហែលមិនត្រូវបានរចនាឡើងសម្រាប់អេក្រង់ចាក់សោរបស់អ្នកទេ និងមិនមានសុវត្ថិភាពឡើយ បើបញ្ចូលទៅទីនេះ។"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"យល់ហើយ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ធាតុ​ក្រាហ្វិក"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"ដើម្បីបញ្ចូលផ្លូវកាត់ \"ធាតុ​ក្រាហ្វិក\" ត្រូវប្រាកដថា \"បង្ហាញធាតុ​ក្រាហ្វិកនៅលើអេក្រង់ចាក់សោ\" ត្រូវបានបើកនៅក្នុងការកំណត់។"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ការកំណត់"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"បង្ហាញប៊ូតុងធាតុ​រក្សា​អេក្រង់"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរ​អ្នក​ប្រើ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ម៉ឺនុយ​ទាញចុះ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យ​ទាំងអស់​ក្នុង​វគ្គ​នេះ​នឹង​ត្រូវ​លុប។"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"កំពុងដំឡើង​កំណែ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"កម្រងព័ត៌មានការងារ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ពេលជិះយន្តហោះ"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"ការគ្រប់គ្រង​ដោយ​មាតាបិតា"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"អ្នកនឹងមិនលឺម៉ោងរោទ៍ <xliff:g id="WHEN">%1$s</xliff:g> បន្ទាប់របស់អ្នកទេ"</string>
     <string name="alarm_template" msgid="2234991538018805736">"នៅ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"នៅ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"បង្ហាញនៅខាងលើ​ការជូនដំណឹងអំពីការសន្ទនា និងជារូបភាព​កម្រង​ព័ត៌មាននៅលើអេក្រង់ចាក់សោ បង្ហាញជាពពុះ បង្អាក់មុខងារកុំ​រំខាន"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"អាទិភាព"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> មិនអាចប្រើ​មុខងារ​សន្ទនា​បានទេ"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"មិនអាច​កែប្រែ​ការជូនដំណឹង​ទាំងនេះ​បានទេ។"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"មិនអាច​កែប្រែ​ការជូនដំណឹងអំពីការហៅទូរសព្ទបានទេ។"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"មិនអាច​កំណត់​រចនាសម្ព័ន្ធ​ក្រុមការជូនដំណឹងនេះ​នៅទីនេះ​បានទេ"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"ប្រតិទិន"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"ម៉ាស៊ីនគិតលេខ"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"ផែនទី"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"កុំ​រំខាន"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ផ្លូវកាត់ប៊ូតុងកម្រិតសំឡេង"</string>
     <string name="battery" msgid="769686279459897127">"ថ្ម"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"កម្មវិធី​ប្រព័ន្ធ"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ការធ្វើកិច្ចការច្រើន"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"មុខងារ​បំបែកអេក្រង់"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ភាពងាយស្រួល"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"វិធីបញ្ចូល"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ផ្លូវកាត់​កម្មវិធី"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"កម្មវិធីបច្ចុប្បន្ន"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"អ្នកបានបញ្ចប់ការមើលចលនាកម្មវិធីថ្មីៗ។"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ដើម្បីមើលកម្មវិធីថ្មីៗ សូមអូសឡើងលើ រួចសង្កត់ឱ្យជាប់លើផ្ទាំងប៉ះរបស់អ្នក ដោយប្រើម្រាមដៃបី"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ប្ដូរ​កម្មវិធី"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ធ្វើបានល្អ!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"អ្នក​បានបញ្ចប់​ចលនា​ប្ដូរកម្មវិធី​ហើយ។"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"មើល​កម្មវិធី​ទាំងអស់"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ចុចគ្រាប់ចុចសកម្មភាពលើក្ដារចុចរបស់អ្នក"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 76f38c0..bfc9d75 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ಲಾಕ್ ಸ್ಕ್ರೀನ್ ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ವಿಜೆಟ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು ಆ್ಯಪ್ ತೆರೆಯಲು, ಇದು ನೀವೇ ಎಂದು ನೀವು ದೃಢೀಕರಿಸಬೇಕಾಗುತ್ತದೆ. ಅಲ್ಲದೆ, ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಆಗಿದ್ದರೂ ಸಹ ಯಾರಾದರೂ ಅವುಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು ಎಂಬುದನ್ನು ನೆನಪಿನಲ್ಲಿಡಿ. ಕೆಲವು ವಿಜೆಟ್‌ಗಳು ನಿಮ್ಮ ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ಗಾಗಿ ಉದ್ದೇಶಿಸದೇ ಇರಬಹುದು ಮತ್ತು ಇಲ್ಲಿ ಸೇರಿಸುವುದು ಸುರಕ್ಷಿತವಲ್ಲದಿರಬಹುದು."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ಅರ್ಥವಾಯಿತು"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ವಿಜೆಟ್‌ಗಳು"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ವಿಜೆಟ್‌ಗಳು\" ಶಾರ್ಟ್‌ಕಟ್ ಸೇರಿಸಲು, ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ \"ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ತೋರಿಸಿ\" ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ಸ್ಕ್ರೀನ್‌ಸೇವರ್ ಬಟನ್ ತೋರಿಸಿ"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"ಹಬ್ ಮೋಡ್ ಅನ್ನು ಎಕ್ಸ್‌ಪ್ಲೋರ್ ಮಾಡಿ"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"ಚಾರ್ಜ್ ಮಾಡುವಾಗ ನಿಮ್ಮ ನೆಚ್ಚಿನ ವಿಜೆಟ್‌ಗಳು ಮತ್ತು ಸ್ಕ್ರೀನ್ ಸೇವರ್‌ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"ಪ್ರಾರಂಭಿಸೋಣ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ಪುಲ್‌ಡೌನ್ ಮೆನು"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಶನ್‌ನಲ್ಲಿನ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ಅಪ್‌ಡೇಟ್‌‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"ಪೋಷಕ ನಿಯಂತ್ರಣಗಳು"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"ನಿಮ್ಮ ಮುಂದಿನ <xliff:g id="WHEN">%1$s</xliff:g> ಅಲಾರಮ್ ಅನ್ನು ನೀವು ಆಲಿಸುವುದಿಲ್ಲ"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ರಲ್ಲಿ"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> ರಂದು"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ಸಂಭಾಷಣೆ ಅಧಿಸೂಚನೆಗಳ ಮೇಲ್ಭಾಗದಲ್ಲಿ ಹಾಗೂ ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ನ ಮೇಲೆ ಪ್ರೊಫೈಲ್ ಚಿತ್ರವಾಗಿ ತೋರಿಸುತ್ತದೆ, ಬಬಲ್‌ನಂತೆ ಗೋಚರಿಸುತ್ತದೆ, ಅಡಚಣೆ ಮಾಡಬೇಡ ಮೋಡ್‌ಗೆ ಅಡ್ಡಿಯುಂಟುಮಾಡುತ್ತದೆ"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ಆದ್ಯತೆ"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"ಸಂವಾದ ಫೀಚರ್‌ಗಳನ್ನು <xliff:g id="APP_NAME">%1$s</xliff:g> ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ಕರೆ ಅಧಿಸೂಚನೆಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"ಈ ಗುಂಪಿನ ಅಧಿಸೂಚನೆಗಳನ್ನು ಇಲ್ಲಿ ಕಾನ್ಫಿಗರ್‌ ಮಾಡಲಾಗಿರುವುದಿಲ್ಲ"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"ಕ್ಯಾಲ್ಕ್ಯುಲೇಟರ್"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ವಾಲ್ಯೂಮ್ ಬಟನ್‌ಗಳ ಶಾರ್ಟ್‌ಕಟ್‌"</string>
     <string name="battery" msgid="769686279459897127">"ಬ್ಯಾಟರಿ"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ಸಿಸ್ಟಂ ಆ್ಯಪ್‌ಗಳು"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ಮಲ್ಟಿಟಾಸ್ಕಿಂಗ್"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ಇನ್‌ಪುಟ್"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ಆ್ಯಪ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ಪ್ರಸ್ತುತ ಆ್ಯಪ್"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ನೀವು ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳ ಜೆಸ್ಚರ್‌ ವೀಕ್ಷಣೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು, ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಿಸಿ"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ನಾಲ್ಕು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ಭೇಷ್!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ನೀವು ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಾಯಿಸುವ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಲು ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ನಾಲ್ಕು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್‌ನಲ್ಲಿ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿ"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ಭೇಷ್!"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index c087e58..b4e3c14 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"잠금 화면 위젯"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"위젯을 사용하여 앱을 열려면 본인 인증을 해야 합니다. 또한 태블릿이 잠겨 있더라도 누구나 볼 수 있다는 점을 유의해야 합니다. 일부 위젯은 잠금 화면에 적합하지 않고 여기에 추가하기에 안전하지 않을 수 있습니다."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"확인"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"위젯"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\'위젯\' 바로가기를 추가하려면 설정에서 \'잠금 화면에 위젯 표시\'가 사용 설정되어 있어야 합니다."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"설정"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"화면 보호기 버튼 표시"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"사용자 전환"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"풀다운 메뉴"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"업데이트 중"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"직장 프로필"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"비행기 모드"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"자녀 보호 기능"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>에 다음 알람을 들을 수 없습니다."</string>
     <string name="alarm_template" msgid="2234991538018805736">"시간: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"일시: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -761,7 +763,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"위성, 연결 상태 양호"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"위성, 연결 가능"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"위성 긴급 SOS"</string>
-    <string name="satellite_emergency_only_carrier_text" msgid="9103913890116841786">"긴급 전화 또는 SOS만 허용"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="9103913890116841786">"긴급 전화 또는 SOS만"</string>
     <string name="accessibility_phone_string_format" msgid="7798841417881811812">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="SIGNAL_STRENGTH_DESCRIPTION">%2$s</xliff:g>."</string>
     <string name="accessibility_no_signal" msgid="7052827511409250167">"신호가 없습니다"</string>
     <string name="accessibility_one_bar" msgid="5342012847647834506">"신호 막대가 1개입니다"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"대화 알림 상단에 표시, 잠금 화면에 프로필 사진으로 표시, 대화창으로 표시, 방해 금지 모드를 무시함"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"우선순위"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱은 대화 기능을 지원하지 않습니다."</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"이 알림은 수정할 수 없습니다."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"전화 알림은 수정할 수 없습니다."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"이 알림 그룹은 여기에서 설정할 수 없습니다."</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"계산기"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"지도"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"방해 금지 모드"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"볼륨 버튼 단축키"</string>
     <string name="battery" msgid="769686279459897127">"배터리"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"시스템 앱"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"멀티태스킹"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"화면 분할"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"접근성"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"입력"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"앱 단축키"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"현재 앱"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"최근 앱 보기 동작을 완료했습니다."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"최근 앱을 보려면 터치패드에서 세 손가락을 사용해 위로 스와이프한 채로 유지하세요."</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"앱 전환"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"잘하셨습니다"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"앱 전환 동작을 완료했습니다."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"모든 앱 보기"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"키보드의 작업 키를 누르세요."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 5d1b604..a3e20a4 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Кулпуланган экрандагы виджеттер"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Колдонмону виджет аркылуу ачуу үчүн өзүңүздү ырасташыңыз керек. Алар кулпуланган планшетиңизде да көрүнүп турат. Кээ бир виджеттерди кулпуланган экранда колдоно албайсыз, андыктан аларды ал жерге кошпой эле койгонуңуз оң."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түшүндүм"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеттер"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджеттер\" ыкчам баскычын кошуу үчүн параметрлерге өтүп, \"Виджеттерди кулпуланган экранда көрсөтүү\" параметри иштетилгенин текшериңиз."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Параметрлер"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Көшөгө баскычын көрсөтүү"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылдый түшүүчү меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана аларга байланыштуу нерселер өчүрүлөт."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Жаңырууда"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Жумуш профили"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Учак режими"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Ата-эненин көзөмөлү"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> боло турган кийинки эскертмени укпайсыз"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> болгондо"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> болгондо"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Cүйлөшүүлөр тууралуу билдирмелердин жогору жагында жана кулпуланган экранда профилдин сүрөтү, ошондой эле калкып чыкма билдирме түрүндө көрүнүп, \"Тынчымды алба\" режимин токтотот"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Маанилүүлүгү"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда оозеки сүйлөшкөнгө болбойт"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Бул билдирмелерди өзгөртүүгө болбойт."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Чалуу билдирмелерин өзгөртүүгө болбойт."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Бул билдирмелердин тобун бул жерде конфигурациялоого болбойт"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Жылнаама"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Эсептегич"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Карталар"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Тынчымды алба"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Үндү көзөмөлдөөчү баскычтардын кыска жолдору"</string>
     <string name="battery" msgid="769686279459897127">"Батарея"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системанын колдонмолору"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Бир нече тапшырма аткаруу"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Экранды бөлүү"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Атайын мүмкүнчүлүктөр"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Киргизүү"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Колдонмонун ыкчам баскычтары"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Учурдагы колдонмо"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Акыркы колдонмолорду көрүү жаңсоосун аткардыңыз."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Соңку колдонмолорду көрүү үчүн сенсордук тактаны үч манжаңыз менен жогору сүрүп, кармап туруңуз"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Колдонмолорду которуштуруу"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Азаматсыз!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"\"Башка колдонмого которулуу жаңсоосу боюнча үйрөткүчтү бүтүрдүңүз."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Бардык колдонмолорду көрүү"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Баскычтобуңуздагы аракет баскычын басыңыз"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 37713b3..eade9f1 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ວິດເຈັດໃນໜ້າຈໍລັອກ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ເພື່ອເປີດແອັບໂດຍໃຊ້ວິດເຈັດ, ທ່ານຈະຕ້ອງຢັ້ງຢືນວ່າແມ່ນທ່ານ. ນອກຈາກນັ້ນ, ກະລຸນາຮັບຊາບວ່າທຸກຄົນສາມາດເບິ່ງຂໍ້ມູນດັ່ງກ່າວໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ. ວິດເຈັດບາງຢ່າງອາດບໍ່ໄດ້ມີໄວ້ສຳລັບໜ້າຈໍລັອກຂອງທ່ານ ແລະ ອາດບໍ່ປອດໄພທີ່ຈະເພີ່ມໃສ່ບ່ອນນີ້."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ເຂົ້າໃຈແລ້ວ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ວິດເຈັດ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"ເພື່ອເພີ່ມທາງລັດ \"ວິດເຈັດ\", ກະລຸນາກວດສອບວ່າໄດ້ເປີດການນຳໃຊ້ \"ສະແດງວິດເຈັດຢູ່ໜ້າຈໍລັອກ\" ໃນການຕັ້ງຄ່າແລ້ວ."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ການຕັ້ງຄ່າ"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ປຸ່ມສະແດງພາບພັກໜ້າຈໍ"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"ສຳຫຼວດໂໝດ Hub"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"ເຂົ້າເຖິງວິດເຈັດ ແລະ ພາບພັກໜ້າຈໍທີ່ທ່ານມັກໃນລະຫວ່າງທີ່ສາກ."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"ມາເລີ່ມກັນເລີຍ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ເມນູແບບດຶງລົງ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯ​ແລະ​ຂໍ້​ມູນ​ທັງ​ໝົດ​ໃນ​ເຊດ​ຊັນ​ນີ້​ຈະ​ຖືກ​ລຶບ​ອອກ."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ກຳລັງອັບເດດ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"​ໂປຣ​ໄຟລ໌​ບ່ອນ​ເຮັດ​ວຽກ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ໂໝດຢູ່ໃນຍົນ"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"ການຄວບຄຸມຂອງພໍ່ແມ່"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"ທ່ານ​ຈະ​ບໍ່​ໄດ້​ຍິນ​ສຽງ​ໂມງ​ປ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"ເວ​ລາ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"ວັນ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ສະແດງຢູ່ເທິງສຸດຂອງການແຈ້ງເຕືອນການສົນທະນາ ແລະ ເປັນຮູບໂປຣໄຟລ໌ຢູ່ໜ້າຈໍລັອກ, ປາກົດເປັນຟອງ, ສະແດງໃນໂໝດຫ້າມລົບກວນໄດ້"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ສຳຄັນ"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່ຮອງຮັບຄຸນສົມບັດການສົນທະນາ"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"ບໍ່ສາມາດແກ້ໄຂການແຈ້ງເຕືອນເຫຼົ່ານີ້ໄດ້."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ບໍ່ສາມາດແກ້ໄຂການແຈ້ງເຕືອນການໂທໄດ້."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"ບໍ່ສາມາດຕັ້ງຄ່າກຸ່ມການແຈ້ງເຕືອນນີ້ຢູ່ບ່ອນນີ້ໄດ້"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"ປະຕິທິນ"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"ຈັກຄິດໄລ່"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"ແຜນທີ່"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ຫ້າມລົບກວນ"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ທາງລັດປຸ່ມສຽງ"</string>
     <string name="battery" msgid="769686279459897127">"ແບັດເຕີຣີ"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ແອັບລະບົບ"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ການເຮັດຫຼາຍໜ້າວຽກພ້ອມກັນ"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ແບ່ງໜ້າຈໍ"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ການຊ່ວຍເຂົ້າເຖິງ"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ອິນພຸດ"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ທາງລັດແອັບ"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ແອັບປັດຈຸບັນ"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ທ່ານເບິ່ງທ່າທາງຂອງແອັບຫຼ້າສຸດສຳເລັດແລ້ວ."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ເພື່ອເບິ່ງແອັບຫຼ້າສຸດ, ໃຫ້ປັດຂຶ້ນແລ້ວຄ້າງໄວ້ໂດຍໃຊ້ສາມນິ້ວເທິງແຜ່ນສຳຜັດຂອງທ່ານ"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ສະຫຼັບແອັບ"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"ໃຊ້ 4 ນິ້ວປັດຂວາເທິງແຜ່ນສຳຜັດຂອງທ່ານ"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ດີຫຼາຍ!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ທ່ານເຮັດທ່າທາງສະຫຼັບແອັບສຳເລັດແລ້ວ."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"ໃຊ້ 4 ນິ້ວປັດຂວາເທິງແຜ່ນສຳຜັດຂອງທ່ານເພື່ອສະຫຼັບແອັບ"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"ເບິ່ງແອັບທັງໝົດ"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ກົດປຸ່ມຄຳສັ່ງຢູ່ແປ້ນພິມຂອງທ່ານ"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ດີຫຼາຍ!"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 4292574..77c92d1 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Užrakinimo ekrano valdikliai"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Kad galėtumėte atidaryti programą naudodami valdiklį, turėsite patvirtinti savo tapatybę. Be to, atminkite, kad bet kas gali peržiūrėti valdiklius net tada, kai planšetinis kompiuteris užrakintas. Kai kurie valdikliai gali būti neskirti jūsų užrakinimo ekranui ir gali būti nesaugu juos čia pridėti."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Supratau"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Valdikliai"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Jei norite pridėti valdiklių šaukinį, patikrinkite, ar nustatymuose įgalinta parinktis „Rodyti valdiklius užrakinimo ekrane“."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nustatymai"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Mygtukas „Rodyti ekrano užsklandą“"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Naršymas centro režimu"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Įkraudami pasiekite mėgstamiausius valdiklius ir ekrano užsklandas."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Pirmyn"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"išplečiamasis meniu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Atnaujinama"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Darbo profilis"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Lėktuvo režimas"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Tėvų kontrolė"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Negirdėsite kito signalo <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,8 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Rodoma pokalbių pranešimų viršuje ir kaip profilio nuotrauka užrakinimo ekrane, burbule, pertraukia netrukdymo režimą"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritetiniai"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ nepalaiko pokalbių funkcijų"</string>
+    <string name="notification_inline_dismiss" msgid="88423586921134258">"Uždaryti"</string>
+    <string name="notification_inline_disable_promotion" msgid="6880961831026048166">"Neberodyti"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Šių pranešimų keisti negalima."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Skambučių pranešimų keisti negalima."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Šios grupės pranešimai čia nekonfigūruojami"</string>
@@ -912,6 +913,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendorius"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Skaičiuotuvas"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Žemėlapiai"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Netrukdymo režimas"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Garsumo mygtukų spartusis klavišas"</string>
     <string name="battery" msgid="769686279459897127">"Akumuliatorius"</string>
@@ -1432,8 +1443,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemos programos"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kelių užduočių atlikimas"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Išskaidyto ekrano režimas"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Pritaikomumas"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Įvestis"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Programos spartieji klavišai"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Esama programa"</string>
@@ -1497,12 +1507,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Atlikote naujausių programų peržiūros gestą."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Peržiūrėkite naujausias programas, jutiklinėje dalyje perbraukę aukštyn trimis pirštais ir palaikę"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Perjungti programas"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Braukite dešinėn keturiais pirštais jutiklinėje dalyje"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Puiku!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Atlikote programų perjungimo gestą."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Braukite dešinėn keturiais pirštais jutiklinėje dalyje, kad perjungtumėte programas"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Žr. visas programas"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paspauskite klaviatūros veiksmų klavišą"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Puikiai padirbėta!"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index cac4275..2443769 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Bloķēšanas ekrāna logrīki"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Lai atvērtu lietotni, izmantojot logrīku, jums būs jāapstiprina sava identitāte. Turklāt ņemiet vērā, ka ikviens var skatīt logrīkus, pat ja planšetdators ir bloķēts. Iespējams, daži logrīki nav paredzēti izmantošanai bloķēšanas ekrānā, un var nebūt droši tos šeit pievienot."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Labi"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Logrīki"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Lai pievienotu saīsni “Logrīki”, iestatījumos noteikti iespējojiet opciju “Rādīt logrīkus bloķēšanas ekrānā”."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Iestatījumi"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Poga “Rādīt ekrānsaudzētāju”"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"novelkamā izvēlne"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Notiek atjaunināšana"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Darba profils"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Lidojuma režīms"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Vecāku kontrole"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nākamais signāls (<xliff:g id="WHEN">%1$s</xliff:g>) netiks atskaņots."</string>
     <string name="alarm_template" msgid="2234991538018805736">"plkst. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Parādās sarunu paziņojumu augšdaļā un kā profila attēls bloķēšanas ekrānā, arī kā burbulis, pārtrauc režīmu “Netraucēt”."</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritārs"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"Lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g> netiek atbalstītas sarunu funkcijas."</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Šos paziņojumus nevar modificēt."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Paziņojumus par zvaniem nevar modificēt."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Šeit nevar konfigurēt šo paziņojumu grupu."</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendārs"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulators"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Kartes"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Režīms “Netraucēt”"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Skaļuma pogu saīsne"</string>
     <string name="battery" msgid="769686279459897127">"Akumulators"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistēmas lietotnes"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Vairākuzdevumu režīms"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekrāna sadalīšana"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Pieejamība"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ievade"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lietotņu saīsnes"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Pašreizējā lietotne"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Jūs sekmīgi veicāt nesen izmantoto lietotņu skatīšanas žestu."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Lai skatītu nesenās lietotnes, skārienpaliktnī ar trīs pirkstiem velciet augšup un turiet"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Pārslēgšanās starp lietotnēm"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Lieliski!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Jūs sekmīgi veicāt pārslēgšanās starp lietotnēm žestu."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Skatīt visas lietotnes"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tastatūrā nospiediet darbību taustiņu."</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index b53cec3..a9aec16 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виџети на заклучен екран"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите апликација со помош на виџет, ќе треба да потврдите дека сте вие. Покрај тоа, имајте предвид дека секој може да ги гледа виџетите, дури и кога вашиот таблет е заклучен. Некои виџети можеби не се наменети за вашиот заклучен екран, па можеби не е безбедно да се додадат овде."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Сфатив"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виџети"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"За да ја додадете кратенката „Виџети“, погрижете се да биде овозможен „Прикажување виџети на заклучен екран“ во „Поставки“."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Поставки"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Копче за прикажување на штедачот на екран"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"паѓачко мени"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијава ќе се избришат."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Се ажурира"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Работен профил"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Авионски режим"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Родителски контроли"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Нема да го слушнете следниот аларм <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"во <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"во <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Се прикажува најгоре во известувањата за разговор и како профилна слика на заклучен екран, се појавува како балонче, го прекинува „Не вознемирувај“"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Приоритетно"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддржува функции за разговор"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Овие известувања не може да се изменат"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Известувањата за повици не може да се изменат."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Оваа група известувања не може да се конфигурира тука"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Календар"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Калкулатор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Карти"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Не вознемирувај"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Кратенка за копчињата за јачина на звук"</string>
     <string name="battery" msgid="769686279459897127">"Батерија"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системски апликации"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мултитаскинг"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Поделен екран"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Пристапност"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Внесување"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Кратенки за апликации"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Тековна апликација"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Го завршивте движењето за прегледување на неодамнешните апликации."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Повлечете нагоре со три прста на допирната подлога и задржете за да ги видите неодамнешните апликации"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Сменете ги апликациите"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Одлично!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Го завршивте движењето за менување апликации."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Прегледајте ги сите апликации"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притиснете го копчето за дејство на тастатурата"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 48ec2c0..4c444f0 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ലോക്ക് സ്‌ക്രീൻ വിജറ്റുകൾ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"വിജറ്റ് ഉപയോഗിച്ച് ഒരു ആപ്പ് തുറക്കാൻ, ഇത് നിങ്ങൾ തന്നെയാണെന്ന് പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. നിങ്ങളുടെ ടാബ്‌ലെറ്റ് ലോക്കായിരിക്കുമ്പോഴും എല്ലാവർക്കും അത് കാണാനാകുമെന്നതും ഓർക്കുക. ചില വിജറ്റുകൾ നിങ്ങളുടെ ലോക്ക് സ്‌ക്രീനിന് ഉള്ളതായിരിക്കില്ല, അവ ഇവിടെ ചേർക്കുന്നത് സുരക്ഷിതവുമായിരിക്കില്ല."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"മനസ്സിലായി"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"വിജറ്റുകൾ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"വിജറ്റുകൾ\" കുറുക്കുവഴി ചേർക്കാൻ, ക്രമീകരണത്തിൽ \"ലോക്ക് സ്‌ക്രീനിൽ വിജറ്റുകൾ കാണിക്കുക\" പ്രവർത്തനക്ഷമമാക്കിയെന്ന് ഉറപ്പാക്കുക."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ക്രമീകരണം"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"സ്‌ക്രീൻ സേവർ കാണിക്കുക ബട്ടൺ"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"ഹബ് മോഡ് അടുത്തറിയുക"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"ചാർജ് ചെയ്യുമ്പോൾ നിങ്ങളുടെ പ്രിയപ്പെട്ട വിജറ്റുകളും സ്‌ക്രീൻ സേവറുകളും ആക്‌സസ് ചെയ്യുക."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"തുടങ്ങാം"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ഉപയോക്താവ് മാറുക"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"പുൾഡൗൺ മെനു"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"അപ്ഡേറ്റ് ചെയ്യുന്നു"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ഫ്ലൈറ്റ് മോഡ്"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"രക്ഷാകർതൃ നിയന്ത്രണങ്ങൾ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-നുള്ള നിങ്ങളുടെ അടുത്ത അലാറം കേൾക്കില്ല"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>-ന്"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-ന്"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"സംഭാഷണ അറിയിപ്പുകളുടെ മുകളിലും സ്ക്രീൻ ലോക്കായിരിക്കുമ്പോൾ ഒരു പ്രൊഫൈൽ ചിത്രമായും ബബിൾ രൂപത്തിൽ ദൃശ്യമാകുന്നു, ശല്യപ്പെടുത്തരുത് മോഡ് തടസ്സപ്പെടുത്തുന്നു"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"മുൻഗണന"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"സംഭാഷണ ഫീച്ചറുകളെ <xliff:g id="APP_NAME">%1$s</xliff:g> പിന്തുണയ്‌ക്കുന്നില്ല"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"ഈ അറിയിപ്പുകൾ പരിഷ്ക്കരിക്കാനാവില്ല."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"കോൾ അറിയിപ്പുകൾ പരിഷ്‌കരിക്കാനാകുന്നില്ല."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"അറിയിപ്പുകളുടെ ഈ ഗ്രൂപ്പ് ഇവിടെ കോണ്‍ഫിഗര്‍ ചെയ്യാൻ കഴിയില്ല"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"കാൽക്കുലേറ്റർ"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ശല്യപ്പെടുത്തരുത്"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"വോളിയം ബട്ടൺ കുറുക്കുവഴി"</string>
     <string name="battery" msgid="769686279459897127">"ബാറ്ററി"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"സിസ്‌റ്റം ആപ്പുകൾ"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"മൾട്ടിടാസ്‌കിംഗ്"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"സ്‌ക്രീൻ വിഭജന മോഡ്"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ഉപയോഗസഹായി"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ഇൻപുട്ട്"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ആപ്പ് കുറുക്കുവഴികൾ"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"നിലവിലെ ആപ്പ്"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക എന്ന ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"സമീപകാല ആപ്പുകൾ കാണുന്നതിന്, നിങ്ങളുടെ ടച്ച്പാഡിൽ മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്‌ത് പിടിക്കുക"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ആപ്പുകൾ മാറുക"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"നിങ്ങളുടെ ടച്ച്‌പാഡിൽ നാല് വിരലുകൾ കൊണ്ട് വലത്തേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"കൊള്ളാം!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ആപ്പുകൾ മാറൽ ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"ആപ്പുകൾ മാറാൻ നിങ്ങളുടെ ടച്ച്പാഡിൽ നാല് വിരലുകൾ കൊണ്ട് വലത്തേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"എല്ലാ ആപ്പുകളും കാണുക"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"നിങ്ങളുടെ കീബോർഡിലെ ആക്ഷൻ കീ അമർത്തുക"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"അഭിനന്ദനങ്ങൾ!"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 6effa0a..3666c0d 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Түгжээтэй дэлгэцийн виджет"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Виджет ашиглан аппыг нээхийн тулд та өөрийгөө мөн болохыг баталгаажуулах шаардлагатай болно. Мөн таны таблет түгжээтэй байсан ч тэдгээрийг дурын хүн үзэж болохыг санаарай. Зарим виджет таны түгжээтэй дэлгэцэд зориулагдаагүй байж магадгүй ба энд нэмэхэд аюултай байж болзошгүй."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ойлголоо"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджет"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджет\"-ийн товчлол нэмэхийн тулд \"Түгжээтэй дэлгэц дээр виджет харуулах\"-ыг тохиргоонд идэвхжүүлсэн эсэхийг нягтална уу."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Тохиргоо"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Дэлгэц амраагчийг харуулах товч"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"эвхмэл цэс"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string>
@@ -553,7 +556,7 @@
     <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Зөвхөн нэг хэрэглэгч үүсгэх боломжтой.}other{Та # хүртэлх хэрэглэгч нэмж болно.}}"</string>
     <string name="user_remove_user_title" msgid="9124124694835811874">"Хэрэглэгчийг устгах уу?"</string>
     <string name="user_remove_user_message" msgid="6702834122128031833">"Энэ хэрэглэгчийн бүх апп болон мэдээлэл устах болно."</string>
-    <string name="user_remove_user_remove" msgid="8387386066949061256">"Арилгах"</string>
+    <string name="user_remove_user_remove" msgid="8387386066949061256">"Хасах"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-тай бичлэг хийж эсвэл дамжуулж эхлэх үү?"</string>
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь бичлэг хийх эсвэл дамжуулах үед таны дэлгэцэд харуулсан эсвэл таны төхөөрөмжөөс тоглуулсан бүх мэдээлэлд хандах эрхтэй байна. Үүнд нууц үг, төлбөрийн дэлгэрэнгүй, зураг, мессеж болон таны тоглуулдаг аудио зэрэг мэдээлэл багтана."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Бичлэг хийж эсвэл дамжуулж эхлэх үү?"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Шинэчилж байна"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Ажлын профайл"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Нислэгийн горим"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Эцэг эхийн хяналт"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-т та дараагийн сэрүүлгээ сонсохгүй"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> цагт"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-т"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Харилцан ярианы мэдэгдлийн дээд талд болон түгжигдсэн дэлгэц дээр профайл зураг байдлаар харуулах бөгөөд бөмбөлөг хэлбэрээр харагдана. Бүү саад бол горимыг тасалдуулна"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Чухал"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь харилцан ярианы онцлогуудыг дэмждэггүй"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Эдгээр мэдэгдлийг өөрчлөх боломжгүй."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Дуудлагын мэдэгдлийг өөрчлөх боломжгүй."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Энэ бүлэг мэдэгдлийг энд тохируулах боломжгүй байна"</string>
@@ -883,7 +889,7 @@
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Аппуудын жагсаалтыг нээх"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Тохиргоог нээх"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Туслахыг нээх"</string>
-    <string name="group_system_lock_screen" msgid="7391191300363416543">"Түгжээтэй дэлгэц"</string>
+    <string name="group_system_lock_screen" msgid="7391191300363416543">"Дэлгэц түгжих"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Тэмдэглэл хөтлөх"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Олон ажил зэрэг хийх"</string>
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Аппыг баруун талд байгаагаар дэлгэцийг хуваахыг ашиглах"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Календарь"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Тооны машин"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Газрын зураг"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Бүү саад бол"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Дууны түвшний товчлуурын товчлол"</string>
     <string name="battery" msgid="769686279459897127">"Батарей"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системийн аппууд"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Олон ажил зэрэг хийх"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Дэлгэцийг хуваах"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Хандалт"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Оролт"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Аппын товчлол"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Одоогийн апп"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Та саяхны аппуудыг харах зангааг гүйцэтгэсэн."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Саяхны аппуудыг харахын тулд мэдрэгч самбар дээрээ гурван хуруугаараа дээш шудраад, удаан дарна уу"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Апп сэлгэх"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Үнэхээр сайн ажиллалаа!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Та апп хооронд сэлгэх зангааг гүйцэтгэлээ."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Бүх аппыг харах"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээрх тусгай товчлуурыг дарна уу"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 77fd538..73deadb 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्‍क्रीन विजेट"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट वापरून अ‍ॅप उघडण्यासाठी, तुम्हाला हे तुम्हीच असल्याची पडताळणी करावी लागेल. तसेच, लक्षात ठेवा, तुमचा टॅबलेट लॉक असतानादेखील कोणीही ती पाहू शकते. काही विजेट कदाचित तुमच्या लॉक स्‍क्रीनसाठी नाहीत आणि ती इथे जोडणे असुरक्षित असू शकते."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"समजले"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेट"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"विजेट\" शॉर्टकट जोडण्यासाठी, सेटिंग्जमध्ये \"लॉक स्‍क्रीनवर विजेट दाखवा\" सुरू असल्याची खात्री करा."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"सेटिंग्ज"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"स्क्रीनसेव्हर दाखवा बटण"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"हब मोड एक्सप्लोर करा"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"चार्ज करत असताना तुमचे आवडते विजेट आणि स्क्रीन सेव्हर अ‍ॅक्सेस करा."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"चला सुरू करू या"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"वापरकर्ता स्विच करा"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनू"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अ‍ॅप्स आणि डेटा हटवला जाईल."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"अपडेट करत आहे"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाईल"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"विमान मोड"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"पालक नियंत्रणे"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"तुम्ही तुमचा <xliff:g id="WHEN">%1$s</xliff:g> वाजता होणारा पुढील अलार्म ऐकणार नाही"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> वाजता"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> रोजी"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"संभाषण सूचनांच्या वरती आणि लॉक स्क्रीनवरील प्रोफाइल फोटो म्हणून दिसते, बबल म्हणून दिसते, व्यत्यय आणू नका यामध्ये अडथळा आणते"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"प्राधान्य"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे संभाषण वैशिष्ट्यांना सपोर्ट करत नाही"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"या सूचनांमध्ये सुधारणा केली जाऊ शकत नाही."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"कॉलशी संबंधित सूचनांमध्ये फेरबदल केला जाऊ शकत नाही."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉन्फिगर केला जाऊ शकत नाही"</string>
@@ -883,7 +886,7 @@
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"ॲप्सची सूची उघडा"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"सेटिंग्ज उघडा"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant उघडा"</string>
-    <string name="group_system_lock_screen" msgid="7391191300363416543">"लॉक स्क्रीन"</string>
+    <string name="group_system_lock_screen" msgid="7391191300363416543">"स्क्रीन लॉक करा"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"नोंद घ्या"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"मल्टिटास्किंग"</string>
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ॲप उजवीकडे ठेवून स्प्लिट स्क्रीन वापरा"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"कॅलेंडर"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"व्यत्यय आणू नका"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"आवाजाच्या बटणांचा शार्टकट"</string>
     <string name="battery" msgid="769686279459897127">"बॅटरी"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टीम अ‍ॅप्स"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टिटास्किंग"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रीन"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"अ‍ॅक्सेसिबिलिटी"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"अ‍ॅप शॉर्टकट"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"सध्याचे अ‍ॅप"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तुम्ही अलीकडील ॲप्स पाहण्याचे जेश्चर पूर्ण केले आहे."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"अलीकडील अ‍ॅप्स पाहण्यासाठी, तुमच्या टचपॅडवर तीन बोटांनी वर स्वाइप करून धरून ठेवा"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"अ‍ॅप्स स्विच करा"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"तुमच्या टचपॅडवर चार बोटांनी उजवीकडे स्‍वाइप करा"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"उत्तम कामगिरी!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"तुम्ही अ‍ॅप्स स्विच करणे जेश्चर पूर्ण केले आहे."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"अ‍ॅप्सदरम्यान स्विच करण्यासाठी तुमच्या टचपॅडवर चार बोटांनी उजवीकडे स्‍वाइप करा"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"सर्व अ‍ॅप्स पहा"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"तुमच्या कीबोर्डवर अ‍ॅक्शन की प्रेस करा"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"खूप छान!"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 7aa95f0..77d2d24 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget skrin kunci"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka apl menggunakan widget, anda perlu mengesahkan identiti anda. Selain itu, perlu diingat bahawa sesiapa sahaja boleh melihat widget tersebut, walaupun semasa tablet anda dikunci. Sesetengah widget mungkin tidak sesuai untuk skrin kunci anda dan mungkin tidak selamat untuk ditambahkan di sini."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Untuk menambahkan pintasan \"Widget\", pastikan \"Tunjukkan widget pada skrin kunci\" didayakan dalam tetapan."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Tetapan"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Tunjukkan butang penyelamat skrin"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Terokai mod hab"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Akses widget dan penyelamat skrin kegemaran anda semasa melakukan pengecasan."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Mari mulakan"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu tarik turun"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mengemaskinikan"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mod pesawat"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Kawalan ibu bapa"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar penggera yang seterusnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Ditunjukkan di bahagian atas pemberitahuan perbualan dan sebagai gambar profil pada skrin kunci, muncul sebagai gelembung, mengganggu Jangan Ganggu"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Keutamaan"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak menyokong ciri perbualan"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Pemberitahuan ini tidak boleh diubah suai."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Pemberitahuan panggilan tidak boleh diubah suai."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Kumpulan pemberitahuan ini tidak boleh dikonfigurasikan di sini"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Jangan Ganggu"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Pintasan butang kelantangan"</string>
     <string name="battery" msgid="769686279459897127">"Bateri"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apl sistem"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Berbilang tugas"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Skrin pisah"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Kebolehaksesan"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan apl"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Apl Semasa"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Anda telah melengkapkan gerak isyarat lihat apl terbaharu."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Untuk melihat apl terbaharu, leret ke atas dan tahan menggunakan tiga jari pada pad sentuh anda"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Tukar apl"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Leret ke kanan menggunakan empat jari pada pad sentuh anda"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bagus!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Anda telah melengkapkan gerak isyarat menukar apl."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Leret ke kanan menggunakan empat jari pada pad sentuh untuk beralih apl"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Lihat semua apl"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan kekunci tindakan pada papan kekunci anda"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Syabas!"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index d973d40..8f44cb0 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"လော့ခ်မျက်နှာပြင် ဝိဂျက်များ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ဝိဂျက်သုံး၍ အက်ပ်ဖွင့်ရန်အတွက် သင်ဖြစ်ကြောင်း အတည်ပြုရန်လိုသည်။ ထို့ပြင် သင့်တက်ဘလက် လော့ခ်ချထားချိန်၌ပင် မည်သူမဆို ၎င်းတို့ကို ကြည့်နိုင်ကြောင်း သတိပြုပါ။ ဝိဂျက်အချို့ကို လော့ခ်မျက်နှာပြင်အတွက် ရည်ရွယ်ထားခြင်း မရှိသဖြင့် ဤနေရာတွင် ထည့်ပါက မလုံခြုံနိုင်ပါ။"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"နားလည်ပြီ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ဝိဂျက်များ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"“ဝိဂျက်များ” ဖြတ်လမ်းလင့်ခ်ထည့်ရန်အတွက် ဆက်တင်များတွင် “လော့ခ်မျက်နှာပြင်ပေါ်၌ ဝိဂျက်များပြရန်” ကိုဖွင့်ထားကြောင်း သေချာပါစေ။"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ဆက်တင်များ"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"စခရင်နားချိန်ပုံ ပြရန်ခလုတ်"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ဆွဲချမီနူး"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"အပ်ဒိတ်လုပ်နေသည်"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"အလုပ် ပရိုဖိုင်"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"လေယာဉ်ပျံမုဒ်"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"မိဘအထိန်းအချုပ်များ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> ၌သင့်နောက်ထပ် နှိုးစက်ကို ကြားမည်မဟုတ်ပါ"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ၌"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> တွင်"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"စကားဝိုင်း အကြောင်းကြားချက်များ၏ ထိပ်ပိုင်းနှင့် ပရိုဖိုင်ပုံအဖြစ် လော့ခ်မျက်နှာပြင်တွင် ပြသည်။ ပူဖောင်းကွက်အဖြစ် မြင်ရပြီး ‘မနှောင့်ယှက်ရ’ ကို ကြားဖြတ်သည်"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ဦးစားပေး"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> က စကားဝိုင်းဝန်ဆောင်မှုများကို မပံ့ပိုးပါ"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"ဤအကြောင်းကြားချက်များကို ပြုပြင်၍ မရပါ။"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ခေါ်ဆိုမှုအကြောင်းကြားချက်များကို ပြင်၍မရပါ။"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"ဤအကြောင်းကြားချက်အုပ်စုကို ဤနေရာတွင် စီစဉ်သတ်မှတ်၍ မရပါ"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"ပြက္ခဒိန်"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"ဂဏန်းတွက်စက်"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"မနှောင့်ယှက်ရ"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"အသံထိန်းချုပ်သည့်ခလုတ် ဖြတ်လမ်း"</string>
     <string name="battery" msgid="769686279459897127">"ဘက်ထရီ"</string>
@@ -1497,11 +1513,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"မကြာသေးမီကအက်ပ်များကို ကြည့်ခြင်းလက်ဟန် သင်ခန်းစာပြီးပါပြီ။"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"လတ်တလောအက်ပ်များကြည့်ရန် တာ့ချ်ပက်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပြီး ဖိထားပါ"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"အက်ပ်များကူးပြောင်းခြင်း"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"တော်ပါပေသည်။"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"အက်ပ်ပြောင်းလက်ဟန် လုပ်ပြီးပါပြီ။"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"အက်ပ်အားလုံးကို ကြည့်ခြင်း"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ကီးဘုတ်တွင် လုပ်ဆောင်ချက်ကီး နှိပ်ပါ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 0d9e276..a264772 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Låseskjermmoduler"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"For å åpne en app ved hjelp av en modul må du bekrefte at det er deg. Husk også at hvem som helst kan se dem, selv om nettbrettet er låst. Noen moduler er kanskje ikke laget for å være på låseskjermen og kan være utrygge å legge til der."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Greit"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Moduler"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"For å legge til «Moduler»-snarveien, sørg for at «Vis moduler på låseskjermen» er slått på i innstillingene."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Innstillinger"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Knapp for å vise skjermspareren"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullegardinmeny"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apper og data i denne økten blir slettet."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Oppdaterer"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Work-profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flymodus"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Foreldrekontroll"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Du hører ikke neste innstilte alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Vises øverst på samtalevarsler og som et profilbilde på låseskjermen, vises som en boble, avbryter «Ikke forstyrr»"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> støtter ikke samtalefunksjoner"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Disse varslene kan ikke endres."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Anropsvarsler kan ikke endres."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Denne varselgruppen kan ikke konfigureres her"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalender"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ikke forstyrr"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Hurtigtast for volumknappene"</string>
     <string name="battery" msgid="769686279459897127">"Batteri"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemapper"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delt skjerm"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Tilgjengelighet"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inndata"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-snarveier"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktiv app"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du har fullført bevegelsen for å se nylige apper."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"For å se nylige apper, sveip opp og hold med tre fingre på styreflaten"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Bytt app"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bra jobbet!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Du har fullført bytt-app-bevegelsen."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Se alle apper"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Trykk på handlingstasten på tastaturet"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index b1911596..ad7ffce 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लक स्क्रिन विजेटहरू"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट प्रयोग गरी एप खोल्न तपाईंले आफ्नो पहिचान पुष्टि गर्नु पर्ने हुन्छ। साथै, तपाईंको ट्याब्लेट लक भएका बेला पनि सबै जनाले तिनलाई देख्न सक्छन् भन्ने कुरा ख्याल गर्नुहोस्। केही विजेटहरू लक स्क्रिनमा प्रयोग गर्ने उद्देश्यले नबनाइएका हुन सक्छन् र तिनलाई यहाँ हाल्नु सुरक्षित नहुन सक्छ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"बुझेँ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेटहरू"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"विजेट\" सर्टकट हाल्न सेटिङमा \"लक स्क्रिनमा विजेट देखाउनुहोस्\" नामक विकल्प अन गरिएको छ भन्ने सुनिश्चित गर्नुहोस्।"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"सेटिङ"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"स्क्रिनसेभर देखाउने बटन"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"प्रयोगकर्ता फेर्नुहोस्"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनु"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"अपडेट गरिँदै छ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाइल"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"हवाइजहाज मोड"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"अभिभावकीय नियन्त्रणहरू"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"तपाईँले आफ्नो अर्को अलार्म <xliff:g id="WHEN">%1$s</xliff:g> सुन्नुहुने छैन"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> मा"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> मा"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"यो वार्तालापका सूचनाहरूको सिरानमा, बबलका रूपमा र लक स्क्रिनमा प्रोफाइल फोटोका रूपमा देखिन्छ। साथै, यसले गर्दा \'बाधा नपुऱ्याउनुहोस्\' नामक सुविधामा अवरोध आउँछ"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा वार्तालापसम्बन्धी सुविधा प्रयोग गर्न मिल्दैन"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"यी सूचनाहरू परिमार्जन गर्न मिल्दैन।"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"कलसम्बन्धी सूचनाहरू परिमार्जन गर्न मिल्दैन।"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"यहाँबाट सूचनाहरूको यो समूह कन्फिगर गर्न सकिँदैन"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"पात्रो"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"क्याल्कुलेटर"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"नक्सा"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"बाधा नपुऱ्याउनुहोस्"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"भोल्युम बटनका सर्टकट"</string>
     <string name="battery" msgid="769686279459897127">"ब्याट्री"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टम एपहरू"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टिटास्किङ"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रिन"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"सर्वसुलभता"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"एपका सर्टकटहरू"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"हालको एप"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तपाईंले जेस्चर प्रयोग गरी हालसालै चलाइएका एपहरू हेर्ने तरिका सिक्नुभएको छ।"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"हालसालैका एपहरू हेर्न तीन औँला प्रयोग गरी टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"एपहरू बदल्नुहोस्"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"अद्भुत!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"तपाईंले एपहरू बदल्ने जेस्चर प्रयोग गर्ने तरिका सिक्नुभयो।"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"सबै एपहरू हेर्नुहोस्"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index a3d8856..b9b025a 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets op het vergrendelscherm"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Als je een app wilt openen met een widget, moet je verifiëren dat jij het bent. Houd er ook rekening mee dat iedereen ze kan bekijken, ook als je tablet vergrendeld is. Bepaalde widgets zijn misschien niet bedoeld voor je vergrendelscherm en kunnen hier niet veilig worden toegevoegd."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Als je de snelkoppeling Widgets wilt toevoegen, zorg je dat Widgets tonen op het vergrendelingsscherm aanstaat in de instellingen."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Instellingen"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Knop Screensaver tonen"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Hub-modus verkennen"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Krijg toegang tot je favoriete widgets en screensavers terwijl je apparaat wordt opgeladen."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Aan de slag"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pull-downmenu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updaten"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuig­modus"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Ouderlijk toezicht"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Je hoort je volgende wekker niet <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"om <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Wordt getoond bovenaan gespreksmeldingen en als profielfoto op het vergrendelscherm, verschijnt als bubbel, onderbreekt Niet storen"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ondersteunt geen gespreksfuncties"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Deze meldingen kunnen niet worden aangepast."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Gespreksmeldingen kunnen niet worden aangepast."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Deze groep meldingen kan hier niet worden ingesteld"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Agenda"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Rekenmachine"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Niet storen"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Volumeknoppen als sneltoets"</string>
     <string name="battery" msgid="769686279459897127">"Batterij"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systeem-apps"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Gesplitst scherm"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Toegankelijkheid"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-sneltoetsen"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Je weet nu hoe je het gebaar Recente apps bekijken maakt."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Als je recente apps wilt bekijken, swipe je met 3 vingers omhoog op de touchpad en houd je vast"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Wisselen tussen apps"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Swipe met 4 vingers naar rechts op de touchpad"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Goed werk!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Je weet nu hoe je het gebaar om van app te wisselen maakt."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Swipe met 4 vingers naar rechts op de touchpad om van app te wisselen"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle apps bekijken"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk op de actietoets op het toetsenbord"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Goed gedaan!"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index fa89a48..c0081de 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ଲକ ସ୍କ୍ରିନ ୱିଜେଟ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ଏକ ୱିଜେଟ ବ୍ୟବହାର କରି ଗୋଟିଏ ଆପ ଖୋଲିବା ପାଇଁ ଏହା ଆପଣ ଅଟନ୍ତି ବୋଲି ଆପଣଙ୍କୁ ଯାଞ୍ଚ କରିବାକୁ ହେବ। ଆହୁରି ମଧ୍ୟ, ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଏହାକୁ ଭ୍ୟୁ କରିପାରିବେ ବୋଲି ମନେ ରଖନ୍ତୁ। କିଛି ୱିଜେଟ ଆପଣଙ୍କ ଲକ ସ୍କ୍ରିନ ପାଇଁ ଉଦ୍ଦିଷ୍ଟ ହୋଇନଥାଇପାରେ ଏବଂ ଏଠାରେ ଯୋଗ କରିବା ଅସୁରକ୍ଷିତ ହୋଇପାରେ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ବୁଝିଗଲି"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ୱିଜେଟ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ୱିଜେଟ\" ସର୍ଟକଟ ଯୋଗ କରିବାକୁ ସେଟିଂସରେ \"ଲକ ସ୍କ୍ରିନରେ ୱିଜେଟଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ\"କୁ ସକ୍ଷମ କରାଯାଇଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ସେଟିଂସ"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ସ୍କ୍ରିନସେଭର ବଟନ ଦେଖାନ୍ତୁ"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍‍ ବଦଳାନ୍ତୁ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ପୁଲଡାଉନ ମେନୁ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍‌ ଓ ଡାଟା ଡିଲିଟ୍‌ ହୋଇଯିବ।"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ଅପଡେଟ ହେଉଛି"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‌"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ଏୟାରପ୍ଲେନ ମୋଡ"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"ବାପାମାଙ୍କ ନିୟନ୍ତ୍ରଣ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>ବେଳେ ଆପଣ ନିଜର ପରବର୍ତ୍ତୀ ଆଲାର୍ମ ଶୁଣିପାରିବେ ନାହିଁ"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ହେଲେ"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> ବେଳେ"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, ଏକ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, \'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\'କୁ ବାଧା ଦିଏ"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ପ୍ରାଥମିକତା"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବାର୍ତ୍ତାଳାପ ଫିଚରଗୁଡ଼ିକୁ ସମର୍ଥନ କରେ ନାହିଁ"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପରିବର୍ତ୍ତନ କରିହେବ ନାହିଁ।"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"କଲ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ପରିବର୍ତ୍ତନ କରାଯାଇପାରିବ ନାହିଁ।"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"ଏଠାରେ ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଗ୍ରୁପ୍ କନଫ୍ୟୁଗର୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"କାଲକୁଲେଟର"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ଭଲ୍ୟୁମ ବଟନ୍‍ ଶର୍ଟକଟ୍‍"</string>
     <string name="battery" msgid="769686279459897127">"ବେଟେରୀ"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ସିଷ୍ଟମ ଆପ୍ସ"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ମଲ୍ଟିଟାସ୍କିଂ"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ଆକ୍ସେସିବିଲିଟୀ"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ଇନପୁଟ"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ଆପ ସର୍ଟକଟ"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ବର୍ତ୍ତମାନର ଆପ"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ଆପଣ ବର୍ତ୍ତମାନର ଆପ୍ସ ଜେଶ୍ଚରକୁ ଭ୍ୟୁ କରିବା ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରିବାକୁ, ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠି ବ୍ୟବହାର କରି ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ଆପ୍ସକୁ ସୁଇଚ କରନ୍ତୁ"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ବଢ଼ିଆ କାମ!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ଆପଣ ସୁଇଚ ଆପ୍ସ ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"ସବୁ ଆପ ଭ୍ୟୁ କରନ୍ତୁ"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ଆପଣଙ୍କର କୀବୋର୍ଡରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index a0c0612..23a94b0 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ਲਾਕ ਸਕ੍ਰੀਨ ਵਿਜੇਟ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ਵਿਜੇਟ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਐਪ ਖੋਲ੍ਹਣ ਲਈ, ਤੁਹਾਨੂੰ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ। ਨਾਲ ਹੀ, ਇਹ ਵੀ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਕੋਈ ਵੀ ਉਨ੍ਹਾਂ ਨੂੰ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਵਿਜੇਟ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ ਲਈ ਨਾ ਬਣੇ ਹੋਣ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਇੱਥੇ ਸ਼ਾਮਲ ਕਰਨਾ ਅਸੁਰੱਖਿਅਤ ਹੋਵੇ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ਸਮਝ ਲਿਆ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ਵਿਜੇਟ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ਵਿਜੇਟ\" ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪੱਕਾ ਕਰੋ ਕਿ ਸੈਟਿੰਗਾਂ ਵਿੱਚ \"ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਦਿਖਾਓ\" ਚਾਲੂ ਹੈ।"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"\'ਸਕ੍ਰੀਨ-ਸੇਵਰ ਦਿਖਾਓ\' ਬਟਨ"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"ਹੱਬ ਮੋਡ ਦੀ ਪੜਚੋਲ ਕਰੋ"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"ਚਾਰਜ ਕਰਨ ਵੇਲੇ ਆਪਣੇ ਮਨਪਸੰਦ ਵਿਜੇਟਾਂ ਅਤੇ ਸਕ੍ਰੀਨ ਸੇਵਰਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰੋ।"</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"ਚਲੋ ਸ਼ੁਰੂ ਕਰੀਏ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ਪੁੱਲਡਾਊਨ ਮੀਨੂ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿਚਲੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟੇ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ਅੱਪਡੇਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"ਮਾਪਿਆਂ ਦੇ ਕੰਟਰੋਲ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"ਤੁਸੀਂ <xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ ਆਪਣਾ ਅਗਲਾ ਅਲਾਰਮ ਨਹੀਂ ਸੁਣੋਗੇ"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਈਆਂ ਜਾਂਦੀਆਂ ਹਨ, ਜੋ ਕਿ ਬਬਲ ਵਜੋਂ ਦਿਸਦੀਆਂ ਹਨ ਅਤੇ \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵਿਘਨ ਵੀ ਪਾ ਸਕਦੀਆਂ ਹਨ"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ਤਰਜੀਹ"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਗੱਲਬਾਤ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਸੋਧਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ਕਾਲ ਸੰਬੰਧੀ ਸੂਚਨਾਵਾਂ ਨੂੰ ਸੋਧਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"ਇਹ ਸੂਚਨਾਵਾਂ ਦਾ ਗਰੁੱਪ ਇੱਥੇ ਸੰਰੂਪਿਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"ਨਕਸ਼ੇ"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ਵੌਲਿਊਮ ਬਟਨ ਸ਼ਾਰਟਕੱਟ"</string>
     <string name="battery" msgid="769686279459897127">"ਬੈਟਰੀ"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ਸਿਸਟਮ ਐਪਾਂ"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ਮਲਟੀਟਾਸਕਿੰਗ"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ਪਹੁੰਚਯੋਗਤਾ"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ਇਨਪੁੱਟ"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ਐਪ ਸ਼ਾਰਟਕੱਟ"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ਮੌਜੂਦਾ ਐਪ"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ਤੁਸੀਂ \'ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ ਹੈ।"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖਣ ਲਈ, ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਵਰਤ ਕੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਕੇ ਰੋਕ ਕੇ ਰੱਖੋ"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ਐਪਾਂ ਵਿਚਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਚਾਰ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ਬਹੁਤ ਵਧੀਆ!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ਤੁਸੀਂ ਐਪ ਸਵਿੱਚ ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"ਐਪਾਂ ਬਦਲਣ ਲਈ ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਚਾਰ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"ਸਾਰੀਆਂ ਐਪਾਂ ਦੇਖੋ"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ਆਪਣੇ ਕੀ-ਬੋਰਡ \'ਤੇ ਕਾਰਵਾਈ ਕੁੰਜੀ ਨੂੰ ਦਬਾਓ"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ਬਹੁਤ ਵਧੀਆ!"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 0313117..fd41419 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widżety na ekranie blokady"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aby otworzyć aplikację za pomocą widżetu, musisz potwierdzić swoją tożsamość. Pamiętaj też, że każdy będzie mógł wyświetlić widżety nawet wtedy, gdy tablet będzie zablokowany. Niektóre widżety mogą nie być przeznaczone do umieszczenia na ekranie blokady i ich dodanie w tym miejscu może być niebezpieczne."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widżety"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Aby dodać skrót „Widżety”, upewnij się, że opcja „Pokaż widżety na ekranie blokady” jest włączona w ustawieniach."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ustawienia"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Przycisk Pokaż wygaszacz ekranu"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Aktualizuję"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil służbowy"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Tryb samolotowy"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Kontrola rodzicielska"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nie usłyszysz swojego następnego alarmu <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"w: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Wyświetla się u góry powiadomień w rozmowach oraz jako zdjęcie profilowe na ekranie blokady, jako dymek, przerywa działanie trybu Nie przeszkadzać"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Priorytetowe"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> nie obsługuje funkcji rozmów"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Tych powiadomień nie można zmodyfikować."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Powiadomień o połączeniach nie można modyfikować."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Tej grupy powiadomień nie można tu skonfigurować"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendarz"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Mapy"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Nie przeszkadzać"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Wł./wył. przyciskami głośności"</string>
     <string name="battery" msgid="769686279459897127">"Bateria"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacje systemowe"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Wielozadaniowość"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podzielony ekran"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Ułatwienia dostępu"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Wprowadzanie"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skróty do aplikacji"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Bieżąca aplikacja"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Znasz już gest wyświetlania ostatnio używanych aplikacji."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Aby wyświetlić ostatnie aplikacje, przesuń 3 palcami w górę na touchpadzie i przytrzymaj"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Przełączanie aplikacji"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Brawo!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Gest do przełączania aplikacji został opanowany."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Wyświetl wszystkie aplikacje"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Naciśnij klawisz działania na klawiaturze"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index e21554c..b31296d 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para adicionar o atalho Widgets, verifique se a opção \"Mostrar widgets na tela de bloqueio\" está ativada nas configurações."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configurações"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botão \"Mostrar protetor de tela\""</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Atualizando"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Controles da família"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Você não ouvirá o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Aparecem na parte superior das notificações de conversa, como uma foto do perfil na tela de bloqueio e como um balão. Interrompem o Não perturbe."</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritárias"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Não é possível modificar essas notificações."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Não é possível modificar as notificações de chamada."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Não é possível configurar esse grupo de notificações aqui"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Agenda"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Mapas"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Não perturbe"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Atalho de botões de volume"</string>
     <string name="battery" msgid="769686279459897127">"Bateria"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Tela dividida"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Acessibilidade"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Você concluiu o gesto para ver os apps recentes."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Se quiser ver os apps recentes, deslize para cima e pressione o touchpad com três dedos"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Mudar de app"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Muito bem!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Você concluiu o gesto para mudar de app."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todos os apps"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index f5c29d4..9187391 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets do ecrã de bloqueio"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir uma app através de um widget, vai ter de validar a sua identidade. Além disso, tenha em atenção que qualquer pessoa pode ver os widgets, mesmo quando o tablet estiver bloqueado. Alguns widgets podem não se destinar ao ecrã de bloqueio e pode ser inseguro adicioná-los aqui."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para adicionar o atalho \"Widgets\", certifique-se de que a opção \"Mostrar widgets no ecrã de bloqueio\" está ativada nas definições."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Definições"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botão Mostrar proteção de ecrã"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Explore o modo Hub"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Aceda aos seus widgets e proteções de ecrã favoritos durante o carregamento."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Começar"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pendente"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"A atualizar"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avião"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Controlos parentais"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Não vai ouvir o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"em <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Aparece na parte superior das notificações de conversas e como uma imagem do perfil no ecrã de bloqueio, surge como um balão, interrompe o modo Não incomodar"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> não suporta funcionalidades de conversa."</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Não é possível modificar estas notificações."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Não é possível modificar as notificações de chamadas."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Não é possível configurar este grupo de notificações aqui."</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendário"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Não incomodar"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Atalho dos botões de volume"</string>
     <string name="battery" msgid="769686279459897127">"Bateria"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ecrã dividido"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Acessibilidade"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Concluiu o gesto para ver as apps recentes."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Para ver as apps recentes, deslize rapidamente para cima sem soltar com 3 dedos no touchpad"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Mudar de app"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Deslize rapidamente para a direita com 4 dedos no touchpad"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Muito bem!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Concluiu o gesto para alternar entre apps."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Deslize rapidamente para a direita com 4 dedos no touchpad para mudar de app"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas as apps"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prima a tecla de ação no teclado"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index e21554c..b31296d 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para adicionar o atalho Widgets, verifique se a opção \"Mostrar widgets na tela de bloqueio\" está ativada nas configurações."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configurações"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Botão \"Mostrar protetor de tela\""</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Atualizando"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Controles da família"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Você não ouvirá o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Aparecem na parte superior das notificações de conversa, como uma foto do perfil na tela de bloqueio e como um balão. Interrompem o Não perturbe."</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritárias"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Não é possível modificar essas notificações."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Não é possível modificar as notificações de chamada."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Não é possível configurar esse grupo de notificações aqui"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Agenda"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Mapas"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Não perturbe"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Atalho de botões de volume"</string>
     <string name="battery" msgid="769686279459897127">"Bateria"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Tela dividida"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Acessibilidade"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Você concluiu o gesto para ver os apps recentes."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Se quiser ver os apps recentes, deslize para cima e pressione o touchpad com três dedos"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Mudar de app"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Muito bem!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Você concluiu o gesto para mudar de app."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todos os apps"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 7a9ea1f..fc23118 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeturi pe ecranul de blocare"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pentru a deschide o aplicație folosind un widget, va trebui să-ți confirmi identitatea. În plus, reține că oricine poate să vadă widgeturile, chiar dacă tableta este blocată. Este posibil ca unele widgeturi să nu fi fost create pentru ecranul de blocare și poate fi nesigur să le adaugi aici."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgeturi"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Pentru a adăuga comanda rapidă Widgeturi, verifică dacă opțiunea Afișează widgeturi pe ecranul de blocare este activată în setări."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Setări"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Butonul Afișează screensaverul"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Se actualizează"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil de serviciu"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mod Avion"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Control parental"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nu vei auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"la <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Se afișează în partea de sus a notificărilor pentru conversații și ca fotografie de profil pe ecranul de blocare, apare ca un balon, întrerupe funcția Nu deranja"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritate"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu acceptă funcții pentru conversații"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Aceste notificări nu pot fi modificate."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Notificările pentru apeluri nu pot fi modificate."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Acest grup de notificări nu poate fi configurat aici"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Nu deranja"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Comandă rapidă din butoanele de volum"</string>
     <string name="battery" msgid="769686279459897127">"Baterie"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicații de sistem"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ecran împărțit"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accesibilitate"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Intrare"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Comenzi rapide pentru aplicații"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicația actuală"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ai finalizat gestul pentru afișarea aplicațiilor recente."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Ca să vezi aplicațiile recente, glisează în sus și ține apăsat cu trei degete pe touchpad"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Comută între aplicații"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Excelent!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ai finalizat gestul de trecere la altă aplicație."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Vezi toate aplicațiile"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Apasă tasta de acțiuni de pe tastatură"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index f5b3374..5b0ce2e 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виджеты на заблокированном экране"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Чтобы открыть приложение, используя виджет, вам нужно будет подтвердить свою личность. Обратите внимание, что виджеты видны всем, даже если планшет заблокирован. Некоторые виджеты не предназначены для использования на заблокированном экране. Добавлять их туда может быть небезопасно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ОК"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеты"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Чтобы создать ярлык \"Виджеты\", убедитесь, что в настройках включена функция \"Показывать виджеты на заблокированном экране\"."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Настройки"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Кнопка \"Показать заставку\""</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Сменить пользователя."</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"раскрывающееся меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Обновление"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Рабочий профиль"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Режим полета"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Родительский контроль"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Следующий будильник: <xliff:g id="WHEN">%1$s</xliff:g>. Звук отключен."</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Появляется в верхней части уведомлений о сообщениях, в виде всплывающего чата, а также в качестве фото профиля на заблокированном экране, прерывает режим \"Не беспокоить\"."</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" не поддерживает функции разговоров."</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Эти уведомления нельзя изменить."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Уведомления о звонках нельзя изменить."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Эту группу уведомлений нельзя настроить здесь."</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Календарь"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Калькулятор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Карты"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Не беспокоить"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Кнопки регулировки громкости"</string>
     <string name="battery" msgid="769686279459897127">"Батарея"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системные приложения"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Многозадачность"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Разделение экрана"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Специальные возможности"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ввод"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Приложения"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Это приложение"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Вы выполнили жест для просмотра недавних приложений."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Чтобы увидеть недавние приложения, проведите по сенсорной панели тремя пальцами вверх и удерживайте."</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Переход в другое приложение"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Отлично!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Вы выполнили жест перехода в другое приложение."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Все приложения"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Нажмите клавишу действия на клавиатуре."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index a9ea2ae..1c25011 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"අගුළු තිර විජට්"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"විජට් එකක් භාවිතයෙන් යෙදුමක් විවෘත කිරීමට, ඔබට ඒ ඔබ බව සත්‍යාපනය කිරීමට අවශ්‍ය වනු ඇත. එසේම, ඔබේ ටැබ්ලටය අගුළු දමා ඇති විට පවා ඕනෑම කෙනෙකුට ඒවා බැලිය හැකි බව මතක තබා ගන්න. සමහර විජට් ඔබේ අගුළු තිරය සඳහා අදහස් කර නොතිබිය හැකි අතර මෙහි එක් කිරීමට අනාරක්ෂිත විය හැක."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"තේරුණා"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"විජට්"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"විජට්\" කෙටිමඟ එක් කිරීමට, සැකසීම් තුළ \"අගුළු තිරයෙහි විජට් පෙන්වන්න\" සබල කර ඇති බවට වග බලා ගන්න."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"සැකසීම්"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"තිර සුරැකුම් බොත්තම පෙන්වන්න"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"පරිශීලක මාරුව"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"නිපතන මෙනුව"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"යාවත්කාලීන කිරීම"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"කාර්යාල පැතිකඩ"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ගුවන්යානා ප්‍රකාරය"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"මාපිය පාලන"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"ඔබට ඔබේ ඊළඟ එලාමය <xliff:g id="WHEN">%1$s</xliff:g> නොඇසෙනු ඇත"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ට"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> දී"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"සංවාද දැනුම්දීම්වල ඉහළින්ම සහ අගුලු තිරයේ ඇති පැතිකඩ පින්තූරයක් ලෙස පෙන්වයි, බුබුළක් ලෙස දිස් වේ, බාධා නොකරන්න සඳහා බාධා කරයි"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ප්‍රමුඛතාව"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> සංවාද විශේෂාංගවලට සහාය නොදක්වයි"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"මෙම දැනුම්දීම් වෙනස් කළ නොහැක."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ඇමතුම් දැනුම්දීම් වෙනස් කළ නොහැකිය."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"මෙම දැනුම්දීම් සමූහය මෙහි වින්‍යාස කළ නොහැක"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"දින දර්ශනය"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"ගණකය"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"සිතියම්"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"බාධා නොකරන්න"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"හඩ පරිමා බොත්තම් කෙටිමග"</string>
     <string name="battery" msgid="769686279459897127">"බැටරිය"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"පද්ධති යෙදුම්"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"බහුකාර්ය"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"බෙදුම් තිරය"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ප්‍රවේශ්‍යතාව"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ආදානය"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"යෙදුම් කෙටිමං"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"වත්මන් යෙදුම"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ඔබ මෑත යෙදුම් ඉංගිත බැලීම සම්පූර්ණ කර ඇත."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"මෑත කාලීන යෙදුම් බැලීමට, ඔබේ ස්පර්ශක පෑඩයේ ඇඟිලි තුනක් භාවිතයෙන් ඉහළට ස්වයිප් කරගෙන සිටින්න"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"යෙදුම් මාරු කරන්න"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"අනර්ඝ වැඩක්!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ඔබ යෙදුම් මාරු කිරීමේ ඉංගිතය සම්පූර්ණ කර ඇත."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"සියලු යෙදුම් බලන්න"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ඔබේ යතුරු පුවරුවේ ක්‍රියාකාරී යතුර ඔබන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 406f423..07eb200 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Miniaplikácie na uzamknutej obrazovke"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ak chcete otvoriť aplikáciu pomocou miniaplikácie, budete musieť overiť svoju totožnosť. Pamätajte, že si miniaplikáciu môže pozrieť ktokoľvek, aj keď máte tablet uzamknutý. Niektoré miniaplikácie možno nie sú určené pre uzamknutú obrazovku a ich pridanie tu môže byť nebezpečné."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Dobre"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Miniaplikácie"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Ak chcete pridať odkaz Miniaplikácie, uistite sa, že v nastaveniach je zapnutá možnosť Zobrazovať miniaplikácie na uzamknutej obrazovke."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavenia"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Zobraziť tlačidlo šetriča obrazovky"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbaľovacia ponuka"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Aktualizuje sa"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Pracovný profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Režim v lietadle"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Rodičovská kontrola"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Váš budík o <xliff:g id="WHEN">%1$s</xliff:g> sa nespustí"</string>
     <string name="alarm_template" msgid="2234991538018805736">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Zobrazuje sa ako bublina v hornej časti upozornení konverzácie a profilová fotka na uzamknutej obrazovke, preruší režim bez vyrušení"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritné"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> nepodporuje funkcie konverzácie"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Tieto upozornenia sa nedajú upraviť."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Upozornenia na hovory sa nedajú upraviť."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Túto skupinu upozornení nejde na tomto mieste konfigurovať"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendár"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulačka"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Mapy"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Režim bez vyrušení"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Skratka tlačidiel hlasitosti"</string>
     <string name="battery" msgid="769686279459897127">"Batéria"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systémové aplikácie"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Rozdelená obrazovka"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Dostupnosť"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skratky aplikácií"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuálna aplikácia"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Použili ste gesto na zobrazenie nedávnych aplikácií."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Ak si chcete zobraziť nedávne aplikácie, potiahnite troma prstami na touchpade nahor a pridržte ich"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Prepínanie aplikácií"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Skvelé!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Dokončili ste gesto na prepnutie aplikácií."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Zobrazenie všetkých aplikácií"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stlačte na klávesnici akčný kláves"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 7127bed..e0c65e3 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Pripomočki na zaklenjenem zaslonu"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Če želite aplikacijo odpreti s pripomočkom, morate potrditi, da ste to vi. Upoštevajte tudi, da si jih lahko ogledajo vsi, tudi ko je tablični računalnik zaklenjen. Nekateri pripomočki morda niso predvideni za uporabo na zaklenjenem zaslonu, zato jih tukaj morda ni varno dodati."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumem"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Pripomočki"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Če želite dodati bližnjico »Pripomočki«, v nastavitvah omogočite možnost »Prikaz pripomočkov na zaklenjenem zaslonu«."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavitve"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Pokaži gumb za ohranjevalnik zaslona"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Raziščite način središča"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Med polnjenjem dostopajte do priljubljenih pripomočkov in ohranjevalnikov zaslona."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Pa začnimo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"spustni meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Posodabljanje"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil za Android Work"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Način za letalo"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Starševski nadzor"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Naslednjega alarma ob <xliff:g id="WHEN">%1$s</xliff:g> ne boste slišali"</string>
     <string name="alarm_template" msgid="2234991538018805736">"ob <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"ob tem času: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,8 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Prikaz v obliki oblačka na vrhu razdelka z obvestili za pogovor in kot profilna slika na zaklenjenem zaslonu, preglasitev načina Ne moti."</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prednostno"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ne podpira pogovornih funkcij."</string>
+    <string name="notification_inline_dismiss" msgid="88423586921134258">"Opusti"</string>
+    <string name="notification_inline_disable_promotion" msgid="6880961831026048166">"Tega ne prikaži več"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Za ta obvestila ni mogoče spremeniti nastavitev."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Obvestil o klicih ni mogoče spreminjati."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Te skupine obvestil ni mogoče konfigurirati tukaj"</string>
@@ -912,6 +913,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Koledar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Računalo"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Zemljevidi"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ne moti"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Bližnjica z gumboma za glasnost"</string>
     <string name="battery" msgid="769686279459897127">"Baterija"</string>
@@ -1432,8 +1443,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Večopravilnost"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Razdeljen zaslon"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Dostopnost"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vnos"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Bližnjice do aplikacij"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
@@ -1497,12 +1507,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvedli ste potezo za ogled nedavnih aplikacij."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Za ogled nedavnih aplikacij povlecite s tremi prsti navzgor po sledilni ploščici in pridržite"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Preklop aplikacij"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Na sledilni ploščici s štirimi prsti povlecite desno"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Odlično!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Izvedli ste potezo za preklop med aplikacijami."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Na sledilni ploščici s štirimi prsti povlecite desno, da preklopite med aplikacijami"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ogled vseh aplikacij"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipko za dejanja na tipkovnici"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index b02434a..f6bd8b5 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Miniaplikacionet në ekranin e kyçjes"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Për të hapur një aplikacion duke përdorur një miniaplikacion, do të duhet të verifikosh që je ti. Ki parasysh gjithashtu që çdo person mund t\'i shikojë, edhe kur tableti yt është i kyçur. Disa miniaplikacione mund të mos jenë planifikuar për ekranin tënd të kyçjes dhe mund të mos jetë e sigurt t\'i shtosh këtu."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"E kuptova"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Miniaplikacionet"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Për të shtuar shkurtoren e \"Miniaplikacioneve\", sigurohu që \"Shfaq miniaplikacionet në ekranin e kyçjes\" të jetë aktivizuar te cilësimet."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Cilësimet"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Shfaq butonin e mbrojtësit të ekranit"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyja me tërheqje poshtë"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string>
@@ -802,6 +805,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shfaqet në krye të njoftimeve të bisedës dhe si fotografia e profilit në ekranin e kyçjes, shfaqet si flluskë dhe ndërpret modalitetin \"Mos shqetëso\""</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Me përparësi"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mbështet veçoritë e bisedës"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Këto njoftime nuk mund të modifikohen."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Njoftimet e telefonatave nuk mund të modifikohen."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ky grup njoftimesh nuk mund të konfigurohet këtu"</string>
@@ -912,6 +919,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendari"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Makina llogaritëse"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Mos shqetëso"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Shkurtorja e butonave të volumit"</string>
     <string name="battery" msgid="769686279459897127">"Bateria"</string>
@@ -1432,8 +1449,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacionet e sistemit"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kryerja e shumë detyrave"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekrani i ndarë"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Qasshmëria"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Hyrja"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Shkurtoret e aplikacionit"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikacioni aktual"</string>
@@ -1497,11 +1513,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Përfundove gjestin për shikimin e aplikacioneve të fundit."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Për të shikuar aplikacionet e fundit, rrëshqit shpejt lart dhe mbaj shtypur me tre gishta në bllokun me prekje"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Ndërro aplikacionet"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Punë e shkëlqyer!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"E ke përfunduar gjestin e ndërrimit të aplikacioneve."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Shiko të gjitha aplikacionet"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Shtyp tastin e veprimit në tastierë"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 7096741..9232888 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виџети за закључани екран"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Да бисте отворили апликацију која користи виџет, треба да потврдите да сте то ви. Имајте у виду да свако може да га види, чак и када је таблет закључан. Неки виџети можда нису намењени за закључани екран и можда није безбедно да их тамо додате."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Важи"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виџети"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Да бисте додали пречицу Виџети, уверите се да је у подешавањима омогућено Приказуј виџете на закључаном екрану."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Подешавања"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Дугме Прикажи чувар екрана"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Истражите режим центра"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Приступајте омиљеним виџетима и чуварима екрана током пуњења."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Идемо"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падајући мени"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ажурира се"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Пословни профил"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Режим рада у авиону"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Родитељски надзор"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Нећете чути следећи аларм у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Приказује се у врху обавештења о конверзацијама и као слика профила на закључаном екрану, појављује се као облачић, прекида режим Не узнемиравај"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Приоритетно"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не подржава функције конверзације"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ова обавештења не могу да се мењају."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Обавештења о позивима не могу да се мењају."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ова група обавештења не може да се конфигурише овде"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Календар"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Калкулатор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Мапе"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Не узнемиравај"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Пречица за дугмад за јачину звука"</string>
     <string name="battery" msgid="769686279459897127">"Батерија"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системске апликације"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Обављање више задатака истовремено"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Подељени екран"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Приступачност"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Унос"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Пречице за апликације"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Актуелна апликација"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Довршили сте покрет за приказивање недавно коришћених апликација."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Да бисте прегледали недавне апликације, превуците нагоре и задржите са три прста на тачпеду"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Пређи на другу апликацију"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Превуците удесно са четири прста на тачпеду"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Одлично!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Довршили сте покрет за промену апликација."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Превуците улево са четири прста на тачпеду да бисте прешли на другу апликацију"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Прикажи све апликације"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притисните тастер радњи на тастатури"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Одлично!"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 4e64e5a..cfa9c0a 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgetar för låsskärm"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Du måste verifiera din identitet innan du öppnar en app med en widget. Tänk också på att alla kan se dem, även när surfplattan är låst. Vissa widgetar kanske inte är avsedda för låsskärmen och det kan vara osäkert att lägga till dem här."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetar"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Om du vill lägga till genvägen Widgetar måste du se till att Visa widgetar på låsskärmen är aktiverat i inställningarna."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Inställningar"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Visa skärmsläckarknappen"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullgardinsmeny"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Uppdaterar"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Jobbprofil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flygplansläge"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Föräldrakontroller"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Nästa alarm, kl. <xliff:g id="WHEN">%1$s</xliff:g>, kommer inte att höras"</string>
     <string name="alarm_template" msgid="2234991538018805736">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Visas högst upp i konversationsaviseringarna och som profilbild på låsskärmen, visas som bubbla, åsidosätter Stör ej"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inte stöd för konversationsfunktioner"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Det går inte att ändra de här aviseringarna."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Det går inte att ändra samtalsaviseringarna."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Den här aviseringsgruppen kan inte konfigureras här"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalender"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkylator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Kartor"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Stör ej"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Genväg till volymknappar"</string>
     <string name="battery" msgid="769686279459897127">"Batteri"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemappar"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multikörning"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delad skärm"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Tillgänglighet"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inmatning"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Genvägar till appar"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuell app"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du är klar med rörelsen för att se de senaste apparna."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Svep uppåt på styrplattan med tre fingrar och håll kvar för att se nyligen använda appar"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Byta app"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bra jobbat!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Du har slutfört rörelsen för att byta app"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Visa alla appar"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryck på åtgärdstangenten på tangentbordet"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index ecba559..9ec763b 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Wijeti zinazoonekana kwenye skrini iliyofungwa"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Utahitaji kuthibitisha kuwa ni wewe ili ufungue programu ukitumia wijeti. Pia, kumbuka kuwa mtu yeyote anaweza kuziona, hata kishikwambi chako kikiwa kimefungwa. Huenda baadhi ya wijeti hazikukusudiwa kutumika kwenye skrini yako iliyofungwa na huenda si salama kuziweka hapa."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Nimeelewa"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Wijeti"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Ili uweke njia ya mkato ya \"Wijeti\", hakikisha kuwa kitufe cha \"Onyesha wijeti kwenye skrini iliyofungwa\" kimewashwa katika mipangilio."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Mipangilio"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Kitufe cha “Onyesha taswira ya skrini”"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyu ya kuvuta chini"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Inasasisha"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Wasifu wa kazini"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Hali ya ndegeni"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Vidhibiti vya wazazi"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Hutasikia kengele yako inayofuata ya saa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"saa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"siku ya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Huonyeshwa kwenye sehemu ya juu ya arifa za mazungumzo na kama picha ya wasifu kwenye skrini iliyofungwa. Huonekana kama kiputo na hukatiza kipengele cha Usinisumbue"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Kipaumbele"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> haitumii vipengele vya mazungumzo"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Arifa hizi haziwezi kubadilishwa."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Arifa za simu haziwezi kubadilishwa."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Kikundi hiki cha arifa hakiwezi kuwekewa mipangilio hapa"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalenda"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kikokotoo"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Ramani"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Usinisumbue"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Njia ya mkato ya vitufe vya sauti"</string>
     <string name="battery" msgid="769686279459897127">"Betri"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Programu za mfumo"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Majukumu mengi"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Gawa skrini"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Ufikivu"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kifaa cha kuingiza data"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Njia za mikato za programu"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Programu Inayotumika Sasa"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Umekamilisha mafunzo ya mguso wa kuangalia programu za hivi majuzi."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Telezesha vidole vitatu juu na ushikilie kwenye padi yako ya kugusa ili uangalie programu za hivi majuzi"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Badilisha programu"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Kazi nzuri!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Umekamilisha mafunzo kuhusu mguso wa kubadili programu."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Angalia programu zote"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Bonyeza kitufe cha vitendo kwenye kibodi yako"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index d6e5be4..bca92d3 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"பூட்டுத் திரை விட்ஜெட்கள்"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"விட்ஜெட்டைப் பயன்படுத்தி ஆப்ஸைத் திறக்க, அது நீங்கள்தான் என்பதை உறுதிசெய்ய வேண்டும். அத்துடன், உங்கள் டேப்லெட் பூட்டப்பட்டிருந்தாலும்கூட அவற்றை யார் வேண்டுமானாலும் பார்க்கலாம் என்பதை நினைவில்கொள்ளுங்கள். சில விட்ஜெட்கள் உங்கள் பூட்டுத் திரைக்காக உருவாக்கப்பட்டவை அல்ல என்பதையும் அவற்றை இங்கே சேர்ப்பது பாதுகாப்பற்றதாக இருக்கக்கூடும் என்பதையும் நினைவில்கொள்ளுங்கள்."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"சரி"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"விட்ஜெட்கள்"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"“விட்ஜெட்கள்” ஷார்ட்கட்டைச் சேர்க்க, அமைப்புகளில் “பூட்டுத் திரையில் விட்ஜெட்களைக் காட்டுதல்” அமைப்பு இயக்கப்பட்டிருப்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"அமைப்புகள்"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ஸ்கிரீன் சேவரைக் காட்டும் பட்டன்"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"பயனரை மாற்று"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"கீழ் இழுக்கும் மெனு"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா ஆப்ஸும் தரவும் நீக்கப்படும்."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"புதுப்பிக்கிறது"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"பணிக் கணக்கு"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"விமானப் பயன்முறை"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"பெற்றோர் கட்டுப்பாடுகள்"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"அடுத்த அலாரத்தை <xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு கேட்க மாட்டீர்கள்"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"உரையாடல் அறிவிப்புகளின் மேற்பகுதியில் காட்டப்படும், திரை பூட்டப்பட்டிருக்கும்போது சுயவிவரப் படமாகக் காட்டப்படும், குமிழாகத் தோன்றும், தொந்தரவு செய்ய வேண்டாம் அம்சம் இயக்கப்பட்டிருக்கும்போதும் காட்டப்படும்"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"முன்னுரிமை"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"உரையாடல் அம்சங்களை <xliff:g id="APP_NAME">%1$s</xliff:g> ஆதரிக்காது"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"இந்த அறிவிப்புகளை மாற்ற இயலாது."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"அழைப்பு அறிவிப்புகளை மாற்ற முடியாது."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"இந்த அறிவுப்புக் குழுக்களை இங்கே உள்ளமைக்க இயலாது"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"கால்குலேட்டர்"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"தொந்தரவு செய்ய வேண்டாம்"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ஒலியளவுப் பொத்தான்களுக்கான ஷார்ட்கட்"</string>
     <string name="battery" msgid="769686279459897127">"பேட்டரி"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"சிஸ்டம் ஆப்ஸ்"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"பல வேலைகளைச் செய்தல்"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"திரைப் பிரிப்பு"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"மாற்றுத்திறன் வசதி"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"உள்ளீடு"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ஆப்ஸ் ஷார்ட்கட்கள்"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"தற்போதைய ஆப்ஸ்"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"சமீபத்தில் பயன்படுத்திய ஆப்ஸுக்கான சைகை பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"சமீபத்திய ஆப்ஸைப் பார்க்க, உங்கள் டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடிக்கவும்"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ஆப்ஸுக்கிடையில் மாறுங்கள்"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"அருமை!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ஆப்ஸுக்கிடையில் மாறும் சைகைப் பயிற்சியை முடித்துவிட்டீர்கள்."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"அனைத்து ஆப்ஸையும் காட்டு"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"உங்கள் கீபோர்டில் ஆக்‌ஷன் பட்டனை அழுத்தவும்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 404e5a1..c439e00 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"లాక్ స్క్రీన్ విడ్జెట్‌లు"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"విడ్జెట్‌ను ఉపయోగించి యాప్‌ను తెరవడానికి, ఇది మీరేనని వెరిఫై చేయాల్సి ఉంటుంది. అలాగే, మీ టాబ్లెట్ లాక్ చేసి ఉన్నప్పటికీ, ఎవరైనా వాటిని చూడగలరని గుర్తుంచుకోండి. కొన్ని విడ్జెట్‌లు మీ లాక్ స్క్రీన్‌కు తగినవి కాకపోవచ్చు, వాటిని ఇక్కడ జోడించడం సురక్షితం కాకపోవచ్చు."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"అర్థమైంది"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"విడ్జెట్‌లు"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"విడ్జెట్‌ల\" షార్ట్‌కట్‌ను జోడించడానికి, సెట్టింగ్‌లలో \"లాక్ స్క్రీన్‌లో విడ్జెట్‌లను చూపండి\" అనే ఆప్షన్‌ను ఎనేబుల్ చేసినట్లు నిర్ధారించుకోండి."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"సెట్టింగ్‌లు"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"స్క్రీన్ సేవర్ బటన్‌ను చూపండి"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"హబ్ మోడ్‌ను అన్వేషించండి"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"ఛార్జింగ్ అయ్యే సమయంలో మీకు ఇష్టమైన విడ్జెట్‌లను, స్క్రీన్ సేవర్‌లను యాక్సెస్ చేయండి."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"ప్రారంభించండి"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"వినియోగదారుని మార్చు"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"పుల్‌డౌన్ మెనూ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్‌లోని అన్ని యాప్‌లు మరియు డేటా తొలగించబడతాయి."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"అప్‌డేట్ చేస్తోంది"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ఆఫీస్ ప్రొఫైల్‌"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"విమానం మోడ్"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"తల్లిదండ్రుల కంట్రోల్స్"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"మీరు <xliff:g id="WHEN">%1$s</xliff:g> సెట్ చేసిన మీ తర్వాత అలారం మీకు వినిపించదు"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>కి"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>కి"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"సంభాషణ నోటిఫికేషన్‌ల ఎగువున, లాక్ స్క్రీన్‌లో ప్రొఫైల్ ఫోటో‌గా చూపిస్తుంది, బబుల్‌గా కనిపిస్తుంది, \'అంతరాయం కలిగించవద్దు\'ను అంతరాయం కలిగిస్తుంది"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ప్రాధాన్యత"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> సంభాషణ ఫీచర్‌లను సపోర్ట్ చేయదు"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"ఈ నోటిఫికేషన్‌లను ఎడిట్ చేయడం వీలుపడదు."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"కాల్ నోటిఫికేషన్‌లను ఎడిట్ చేయడం సాధ్యం కాదు."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"ఈ నోటిఫికేషన్‌ల గ్రూప్‌ను ఇక్కడ కాన్ఫిగర్ చేయలేము"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"క్యాలిక్యులేటర్"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"అంతరాయం కలిగించవద్దు"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"వాల్యూమ్ బటన్‌ల షార్ట్‌కట్"</string>
     <string name="battery" msgid="769686279459897127">"బ్యాటరీ"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"సిస్టమ్ యాప్‌లు"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"మల్టీ-టాస్కింగ్"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"స్ప్లిట్ స్క్రీన్"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"యాక్సెసిబిలిటీ"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ఇన్‌పుట్"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"యాప్ షార్ట్‌కట్‌లు"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ప్రస్తుత యాప్"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ఇటీవలి యాప్‌లను చూడడానికి ఉపయోగించే సంజ్ఞకు సంబంధించిన ట్యుటోరియల్‌ను మీరు పూర్తి చేశారు."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ఇటీవలి యాప్‌లను చూడటానికి, మీ టచ్‌ప్యాడ్‌లో మూడు వేళ్లను ఉపయోగించి పైకి స్వైప్ చేసి, హోల్డ్ చేయండి"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"యాప్‌ల మధ్య మారండి"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"మీ టచ్‌ప్యాడ్‌లో నాలుగు వేళ్లను ఉపయోగించి కుడి వైపునకు స్వైప్ చేయండి"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"చక్కగా పూర్తి చేశారు!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"మీరు యాప్‌ల మధ్య మారేందుకు సంజ్ఞను పూర్తి చేశారు."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"యాప్‌ల మధ్య మారేందుకు మీ టచ్‌ప్యాడ్‌లో నాలుగు వేళ్లను ఉపయోగించి కుడి వైపునకు స్వైప్ చేయండి"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"అన్ని యాప్‌లను చూడండి"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"చక్కగా చేశారు!"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index c67852b..7eef449 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"วิดเจ็ตในหน้าจอล็อก"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"หากต้องการเปิดแอปโดยใช้วิดเจ็ต คุณจะต้องยืนยันตัวตนของคุณ นอกจากนี้ โปรดทราบว่าผู้อื่นจะดูวิดเจ็ตเหล่านี้ได้แม้ว่าแท็บเล็ตจะล็อกอยู่ก็ตาม วิดเจ็ตบางอย่างอาจไม่ได้มีไว้สำหรับหน้าจอล็อกของคุณ และอาจไม่ปลอดภัยที่จะเพิ่มที่นี่"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"รับทราบ"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"วิดเจ็ต"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"หากต้องการเพิ่มทางลัด \"วิดเจ็ต\" โปรดตรวจสอบว่าได้เปิดใช้ \"แสดงวิดเจ็ตในหน้าจอล็อก\" แล้วในการตั้งค่า"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"การตั้งค่า"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"ปุ่มแสดงภาพพักหน้าจอ"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"สำรวจโหมดฮับ"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"เข้าถึงวิดเจ็ตและภาพพักหน้าจอโปรดขณะชาร์จ"</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"มาเริ่มกันเลย"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"สลับผู้ใช้"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"เมนูแบบเลื่อนลง"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"กำลังอัปเดต"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"โปรไฟล์งาน"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"โหมดบนเครื่องบิน"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"การควบคุมโดยผู้ปกครอง"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"คุณจะไม่ได้ยินเสียงปลุกครั้งถัดไปในเวลา <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"เวลา <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"ในวันที่ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,8 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"แสดงที่ด้านบนของการแจ้งเตือนการสนทนาและเป็นรูปโปรไฟล์บนหน้าจอล็อก ปรากฏเป็นบับเบิล แสดงในโหมดห้ามรบกวน"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"สำคัญ"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่รองรับฟีเจอร์การสนทนา"</string>
+    <string name="notification_inline_dismiss" msgid="88423586921134258">"ปิด"</string>
+    <string name="notification_inline_disable_promotion" msgid="6880961831026048166">"ไม่ต้องแสดงอีก"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"แก้ไขการแจ้งเตือนเหล่านี้ไม่ได้"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"แก้ไขการแจ้งเตือนสายเรียกเข้าไม่ได้"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"การแจ้งเตือนกลุ่มนี้กำหนดค่าที่นี่ไม่ได้"</string>
@@ -912,6 +913,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"ปฏิทิน"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"เครื่องคิดเลข"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"แผนที่"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ห้ามรบกวน"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ทางลัดปุ่มปรับระดับเสียง"</string>
     <string name="battery" msgid="769686279459897127">"แบตเตอรี่"</string>
@@ -1432,8 +1443,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"แอประบบ"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"การทํางานหลายอย่างพร้อมกัน"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"แยกหน้าจอ"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"การช่วยเหลือพิเศษ"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"อินพุต"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"แป้นพิมพ์ลัดของแอป"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"แอปปัจจุบัน"</string>
@@ -1497,12 +1507,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"คุณทำท่าทางสัมผัสเพื่อดูแอปล่าสุดสำเร็จแล้ว"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"หากต้องการดูแอปล่าสุด ให้ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้บนทัชแพด"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"เปลี่ยนแอป"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"ใช้ 4 นิ้วปัดไปทางขวาบนทัชแพด"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"เก่งมาก"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"คุณทำท่าทางสัมผัสเพื่อสลับแอปเสร็จแล้ว"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"ใช้ 4 นิ้วปัดไปทางขวาบนทัชแพดเพื่อเปลี่ยนแอป"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"ดูแอปทั้งหมด"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ยอดเยี่ยม"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index cf2b2f0..154ba65 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Mga widget ng lock screen"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para magbukas ng app gamit ang isang widget, kakailanganin mong i-verify na ikaw iyan. Bukod pa rito, tandaang puwedeng tingnan ng kahit na sino ang mga ito, kahit na naka-lock ang iyong tablet. Posibleng hindi para sa iyong lock screen ang ilang widget at posibleng hindi ligtas ang mga ito na idagdag dito."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Mga Widget"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para idagdag ang shortcut na \"Mga Widget,\" tiyaking naka-enable ang \"Ipakita ang mga widget sa lock screen\" sa mga setting."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Mga Setting"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Button na ipakita ang screensaver"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"I-explore ang hub mode"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"I-access ang mga paborito mong widget at screen saver habang nagcha-charge."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Tara na"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ina-update"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profile sa trabaho"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Parental controls"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Hindi mo maririnig ang iyong susunod na alarm ng <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"ng <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"sa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Makikita sa itaas ng mga notification ng pag-uusap at bilang larawan sa profile sa lock screen, lumalabas bilang bubble, naaabala ang Huwag Istorbohin"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Priyoridad"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"Hindi sinusuportahan ng <xliff:g id="APP_NAME">%1$s</xliff:g> ang mga feature ng pag-uusap"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Hindi puwedeng baguhin ang mga notification na ito."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Hindi mabago ang mga notification ng tawag."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Hindi mako-configure dito ang pangkat na ito ng mga notification"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Mga mapa"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Huwag Istorbohin"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Shortcut ng mga button ng volume"</string>
     <string name="battery" msgid="769686279459897127">"Baterya"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Mga system app"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Pag-multitask"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Accessibility"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Mga shortcut ng app"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Kasalukuyang App"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Nakumpleto mo ang galaw sa pag-view ng mga kamakailang app."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Para tingnan ang mga kamakailang app, mag-swipe pataas at i-hold gamit ang tatlong daliri sa iyong touchpad"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Lumipat ng app"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Mag-swipe pakanan gamit ang apat na daliri sa iyong touchpad"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Magaling!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Nakumpleto mo na ang galaw para magpalipat-lipat sa mga app."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Mag-swipe pakanan gamit ang apat na daliri sa iyong touchpad para lumipat ng app"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Tingnan ang lahat ng app"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pindutin ang action key sa iyong keyboard"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Magaling!"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 8cd9eb3..d532bef 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kilit ekranı widget\'ları"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Widget kullanarak bir uygulamayı açmak için kimliğinizi doğrulamanız gerekir. Ayrıca, tabletiniz kilitliyken bile widget\'ların herkes tarafından görüntülenebileceğini unutmayın. Bazı widget\'lar kilit ekranınız için tasarlanmamış olabileceğinden buraya eklenmeleri güvenli olmayabilir."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget\'lar"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Widget\'lar\" kısayolunu eklemek için ayarlarda \"Widget\'ları kilit ekranında göster\" seçeneğinin etkinleştirildiğinden emin olun."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ayarlar"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Ekran koruyucuyu göster düğmesi"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değiştirme"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"açılır menü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Güncelleniyor"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Uçak modu"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Ebeveyn denetimleri"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> olarak ayarlanmış bir sonraki alarmınızı duymayacaksınız"</string>
     <string name="alarm_template" msgid="2234991538018805736">"saat: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"gün ve saat: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Görüşme bildirimlerinin üstünde ve kilit ekranında profil resmi olarak gösterilir, baloncuk olarak görünür, Rahatsız Etmeyin\'i kesintiye uğratır"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Öncelikli"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>, sohbet özelliklerini desteklemiyor"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Bu bildirimler değiştirilemez."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Arama bildirimleri değiştirilemez."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Bu bildirim grubu burada yapılandırılamaz"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Takvim"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Hesap Makinesi"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Haritalar"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Rahatsız Etmeyin"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Ses düğmeleri kısayolu"</string>
     <string name="battery" msgid="769686279459897127">"Pil"</string>
@@ -1497,11 +1513,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Son uygulamaları görüntüleme hareketini tamamladınız."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Son kullanılan uygulamaları görüntülemek için dokunmatik alanda üç parmağınızla yukarı kaydırıp basılı tutun"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Uygulamalar arasında geçiş yapma"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Tebrikler!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Uygulamalar arasında geçiş yapma hareketini tamamladınız."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Tüm uygulamaları göster"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klavyenizde eylem tuşuna basın"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index bd877f3..c3a01f5 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Віджети для заблокованого екрана"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Щоб відкрити додаток за допомогою віджета, вам потрібно буде підтвердити особу. Пам’ятайте також, що бачити віджети можуть усі, навіть коли планшет заблоковано. Можливо, деякі віджети не призначені для заблокованого екрана, і додавати їх на нього може бути небезпечно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Віджети"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Щоб додати ярлик \"Віджети\", переконайтеся, що в налаштуваннях увімкнено опцію \"Показувати віджети на заблокованому екрані\"."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Налаштування"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Кнопка \"Показати заставку\""</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Змінити користувача"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"спадне меню"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string>
@@ -802,6 +805,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"З’являється вгорі сповіщень про розмови і як зображення профілю на заблокованому екрані, відображається як спливаючий чат, перериває режим \"Не турбувати\""</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Пріоритет"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не підтримує функції розмов"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ці сповіщення не можна змінити."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Сповіщення про виклик не можна змінити."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Цю групу сповіщень не можна налаштувати тут"</string>
@@ -912,6 +919,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Календар"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Калькулятор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Карти"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Не турбувати"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Кнопки гучності на корпусі"</string>
     <string name="battery" msgid="769686279459897127">"Акумулятор"</string>
@@ -1497,11 +1514,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ви виконали жест для перегляду нещодавно відкритих додатків."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Щоб переглянути останні додатки, проведіть трьома пальцями вгору й утримуйте їх на сенсорній панелі"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Перемикання між додатками"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Чудово!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ви виконали жест перемикання додатків."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Переглянути всі додатки"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натисніть клавішу дії на клавіатурі"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index d3900f2..bbde335 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"مقفل اسکرین کے ویجیٹس"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ویجیٹ کے ذریعے ایپ کھولنے کے لیے آپ کو تصدیق کرنی ہوگی کہ یہ آپ ہی ہیں۔ نیز، ذہن میں رکھیں کہ کوئی بھی انہیں دیکھ سکتا ہے، یہاں تک کہ جب آپ کا ٹیبلیٹ مقفل ہو۔ ہو سکتا ہے کچھ ویجٹس آپ کی لاک اسکرین کے لیے نہ بنائے گئے ہوں اور یہاں شامل کرنا غیر محفوظ ہو سکتا ہے۔"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"سمجھ آ گئی"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ویجیٹس"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ویجیٹس\" شارٹ کٹ شامل کرنے کے لیے، یقینی بنائیں کہ \"مقفل اسکرین پر ویجیٹس دکھائیں\" ترتیبات میں فعال ہے۔"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ترتیبات"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"اسکرین سیور بٹن دکھائیں"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"ہب موڈ دریافت کریں"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"چارج کرتے وقت اپنے پسندیدہ ویجیٹس اور اسکرین سیورز تک رسائی حاصل کریں۔"</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"آئیے شروع کریں"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"صارف سوئچ کریں"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"پل ڈاؤن مینیو"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"اپ ڈیٹ ہو رہا ہے"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"دفتری پروفائل"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ہوائی جہاز وضع"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"پیرنٹل کنٹرولز"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"آپ کو <xliff:g id="WHEN">%1$s</xliff:g> بجے اپنا اگلا الارم سنائی نہیں دے گا"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> بجے"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> بجے"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"یہ گفتگو کی اطلاعات کے اوپری حصّے پر اور مقفل اسکرین پر پروفائل کی تصویر کے بطور دکھائی دیتا ہے، بلبلے کے بطور ظاہر ہوتا ہے، \'ڈسٹرب نہ کریں\' میں مداخلت کرتا ہے"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ترجیح"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ گفتگو کی خصوصیات کو سپورٹ نہیں کرتی ہے"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"ان اطلاعات کی ترمیم نہیں کی جا سکتی۔"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"کال کی اطلاعات میں ترمیم نہیں کی جا سکتی۔"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"اطلاعات کے اس گروپ کو یہاں کنفیگر نہیں کیا جا سکتا"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"کیلنڈر"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"کیلکولیٹر"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ڈسٹرب نہ کریں"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"والیوم بٹنز کے شارٹ کٹ"</string>
     <string name="battery" msgid="769686279459897127">"بیٹری"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"سسٹم ایپس"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ملٹی ٹاسکنگ"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"اسپلٹ اسکرین"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"ایکسیسبیلٹی"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ان پٹ"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ایپ شارٹ کٹس"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"موجودہ ایپ"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"آپ نے حالیہ ایپس دیکھیں کا اشارہ مکمل کر لیا ہے۔"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"حالیہ ایپس دیکھنے کے لیے، اپنے ٹچ پیڈ پر تین انگلیوں کی مدد سے اوپر کی طرف سوائپ کریں اور دبائے رکھیں"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ایپس سوئچ کریں"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"اپنے ٹچ پیڈ پر چار انگلیوں کا استعمال کرتے ہوئے بائیں طرف سوائپ کریں"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"بہترین!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"آپ نے ایپس کے مابین سوئچ کرنے کا اشارہ مکمل کر لیا۔"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"ایپس سوئچ کرنے کے لیے اپنے ٹچ پیڈ پر چار انگلیوں کا استعمال کرتے ہوئے بائیں طرف سوائپ کریں"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"سبھی ایپس دیکھیں"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اپنے کی بورڈ پر ایکشن کلید دبائیں"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"بہت خوب!"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 7006abb..ea4a7c7 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Ekran qulfi vidjetlari"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ilovani vidjet orqali ochish uchun shaxsingizni tasdiqlashingiz kerak. Shuningdek, planshet qulflanganda ham bu axborotlar hammaga koʻrinishini unutmang. Ayrim vidjetlar ekran qulfiga moslanmagan va ularni bu yerda chiqarish xavfli boʻlishi mumkin."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidjetlar"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"“Vidjetlar” yorligʻini qoʻshish uchun sozlamalarda “Vidjetlarni ekran qulfida chiqarish” yoqilganini tekshiring."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Sozlamalar"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Ekran lavhasi tugmasini chiqarish"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"Hub rejimi bilan tanishing"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"Quvvatlash paytida sevimli vidjetlar va ekran lavhalaridan foydalaning."</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"Boshlash"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Foydalanuvchini almashtirish"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"tortib tushiriladigan menyu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Yangilanmoqda"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Ish profili"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Parvoz rejimi"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Ota-ona nazorati"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Keyingi signal (<xliff:g id="WHEN">%1$s</xliff:g>) chalinmaydi"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,8 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Suhbat bildirishnomalari tepasida va ekran qulfida profil rasmi sifatida chiqariladi, bulutcha sifatida chiqadi, Bezovta qilinmasin rejimini bekor qiladi"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Muhim"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasida suhbat funksiyalari ishlamaydi"</string>
+    <string name="notification_inline_dismiss" msgid="88423586921134258">"Yopish"</string>
+    <string name="notification_inline_disable_promotion" msgid="6880961831026048166">"Boshqa chiqmasin"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Bu bildirishnomalarni tahrirlash imkonsiz."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Chaqiruv bildirishnomalarini tahrirlash imkonsiz."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ushbu bildirishnomalar guruhi bu yerda sozlanmaydi"</string>
@@ -912,6 +913,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Taqvim"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulyator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Xaritalar"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Bezovta qilinmasin"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Ovoz balandligini boshqarish tugmalari"</string>
     <string name="battery" msgid="769686279459897127">"Batareya"</string>
@@ -1432,8 +1443,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Tizim ilovalari"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multi-vazifalilik"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekranni ikkiga ajratish"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Qulaylik"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kiritish"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ilova yorliqlari"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Joriy ilova"</string>
@@ -1497,12 +1507,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Oxirgi ilovalarni koʻrish ishorasini tugalladingiz."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Oxirgi ochilgan ilovalarni koʻrish uchun sensorli panelda uchta barmoq bilan tepega surib, ushlab turing"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Ilovalarni almashtirish"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"Sensorli panelda toʻrtta barmoq bilan oʻngga suring"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Barakalla!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ilovalarni almashtirish darsini tamomladingiz."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"Ilovalarni almashtirish uchun sensorli panelda toʻrtta barmoq bilan oʻngga suring"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Barcha ilovalarni koʻrish"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturadagi amal tugmasini bosing"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Barakalla!"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 1885742..5380afa 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Tiện ích trên màn hình khoá"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Để dùng tiện ích mở một ứng dụng, bạn cần xác minh danh tính của mình. Ngoài ra, hãy lưu ý rằng bất kỳ ai cũng có thể xem các tiện ích này, ngay cả khi máy tính bảng của bạn được khoá. Một số tiện ích có thể không dành cho màn hình khoá và không an toàn khi thêm vào đây."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Tôi hiểu"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Tiện ích"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Để thêm phím tắt \"Tiện ích\", hãy nhớ bật tuỳ chọn \"Hiện tiện ích trên màn hình khoá\" trong phần cài đặt."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Cài đặt"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Hiện nút trình bảo vệ màn hình"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"trình đơn kéo xuống"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Đang cập nhật"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Hồ sơ công việc"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Chế độ trên máy bay"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Chế độ kiểm soát của cha mẹ"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Bạn sẽ không nghe thấy chuông báo tiếp theo lúc <xliff:g id="WHEN">%1$s</xliff:g> của mình"</string>
     <string name="alarm_template" msgid="2234991538018805736">"lúc <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"vào <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Hiện ở đầu phần thông báo cuộc trò chuyện và ở dạng ảnh hồ sơ trên màn hình khóa, xuất hiện ở dạng bong bóng, làm gián đoạn chế độ Không làm phiền"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Mức độ ưu tiên"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> không hỗ trợ các tính năng trò chuyện"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Không thể sửa đổi các thông báo này."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Không thể sửa đổi các thông báo cuộc gọi."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Không thể định cấu hình nhóm thông báo này tại đây"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Lịch"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Máy tính"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Bản đồ"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Không làm phiền"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Phím tắt các nút âm lượng"</string>
     <string name="battery" msgid="769686279459897127">"Pin"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Ứng dụng hệ thống"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Đa nhiệm"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Chia đôi màn hình"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Hỗ trợ tiếp cận"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Phương thức nhập"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Phím tắt cho ứng dụng"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Ứng dụng hiện tại"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Bạn đã hoàn tất cử chỉ xem ứng dụng gần đây."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Để xem các ứng dụng gần đây, hãy dùng 3 ngón tay vuốt lên và giữ trên bàn di chuột"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Chuyển đổi ứng dụng"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Tuyệt vời!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Bạn đã thực hiện xong cử chỉ chuyển đổi ứng dụng."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Xem tất cả các ứng dụng"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nhấn phím hành động trên bàn phím"</string>
diff --git a/packages/SystemUI/res/values-xlarge-land/config.xml b/packages/SystemUI/res/values-xlarge-land/config.xml
index 5e4304e..6d8b64a 100644
--- a/packages/SystemUI/res/values-xlarge-land/config.xml
+++ b/packages/SystemUI/res/values-xlarge-land/config.xml
@@ -16,4 +16,5 @@
 
 <resources>
     <item name="shortcut_helper_screen_width_fraction"  format="float" type="dimen">0.8</item>
+    <bool name="center_align_magic_portrait_shape">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 53c825b..4b29fb6 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -535,10 +535,10 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"锁屏微件"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"若要使用微件打开应用,您需要验证是您本人在操作。另外请注意,任何人都可以查看此类微件,即使您的平板电脑已锁定。有些微件可能不适合显示在锁定的屏幕中,因此添加到这里可能不安全。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"微件"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"如要添加“微件”快捷方式,请确保已在设置中启用“在锁屏状态下显示微件”。"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"设置"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"“显示屏保”按钮"</string>
+    <string name="hub_onboarding_bottom_sheet_title" msgid="162092881395529947">"探索基座接入模式"</string>
+    <string name="hub_onboarding_bottom_sheet_text" msgid="8589816797970240544">"充电时访问您喜爱的微件和屏保。"</string>
+    <string name="hub_onboarding_bottom_sheet_action_button" msgid="6161983690157872829">"现在就试试吧"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉菜单"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
@@ -750,8 +750,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"正在更新"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"工作资料"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"飞行模式"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"家长控制"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"您在<xliff:g id="WHEN">%1$s</xliff:g>将不会听到下次闹钟响铃"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +801,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"以气泡形式显示在对话通知顶部(屏幕锁定时显示为个人资料照片),并且会中断勿扰模式"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"优先"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>不支持对话功能"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"无法修改这些通知。"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"无法修改来电通知。"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"您无法在此处配置这组通知"</string>
@@ -912,6 +915,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"日历"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"计算器"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"地图"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"勿扰"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"音量按钮快捷键"</string>
     <string name="battery" msgid="769686279459897127">"电池"</string>
@@ -1432,8 +1445,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系统应用"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多任务处理"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分屏"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"无障碍"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"输入"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"应用快捷键"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"当前应用"</string>
@@ -1497,12 +1509,10 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"您已完成“查看最近用过的应用”的手势教程。"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"如需查看最近用过的应用,请用三根手指在触控板上向上滑动并按住"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"切换应用"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
-    <skip />
+    <string name="touchpad_switch_apps_gesture_guidance" msgid="2554844933805502538">"在触控板上用四根手指向右滑动"</string>
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"太棒了!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"您完成了应用切换手势教程。"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
-    <skip />
+    <string name="touchpad_switch_gesture_error_body" msgid="5895231916964677918">"在触控板上用四根手指向右滑动可切换应用"</string>
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有应用"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按键盘上的快捷操作按键"</string>
     <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常棒!"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index aa539d6..1605840 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"上鎖畫面小工具"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,所有人都能查看小工具,即使平板電腦已鎖定亦然。部分小工具可能不適用於上鎖畫面,新增至這裡可能會有安全疑慮。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"小工具"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"如要新增「小工具」捷徑,請確保在設定中已啟用「在上鎖畫面顯示小工具」。"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"設定"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"顯示螢幕保護程式按鈕"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"正在更新"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"工作設定檔"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"飛行模式"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"家長監控設定"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"你不會<xliff:g id="WHEN">%1$s</xliff:g>聽到鬧鐘"</string>
     <string name="alarm_template" msgid="2234991538018805736">"在 <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"在<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"以對話氣泡形式顯示在對話通知頂部 (在上鎖畫面會顯示為個人檔案相片),並會中斷「請勿打擾」模式"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"優先"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」不支援對話功能"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"無法修改這些通知。"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"無法修改通話通知。"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"無法在此設定這組通知"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"日曆"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"計算機"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"地圖"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"請勿騷擾"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"音量按鈕快速鍵"</string>
     <string name="battery" msgid="769686279459897127">"電池"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系統應用程式"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割螢幕"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"無障礙功能"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"輸入"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"應用程式捷徑"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"目前的應用程式"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"你已完成「查看最近使用的應用程式」手勢的教學課程。"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"如要查看最近使用的應用程式,請用三隻手指在觸控板上向上滑動並按住"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"切換應用程式"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"做得好!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"你已完成「應用程式切換手勢」的教學課程。"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有應用程式"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 2d6ac8d..cbdb641 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -535,10 +535,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"螢幕鎖定小工具"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,需先驗證身分。請留意,即使平板電腦已鎖定,所有人都還是能查看小工具。某些小工具可能不適用於螢幕鎖定畫面,新增到此可能會有安全疑慮。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"我知道了"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"小工具"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"如要新增「小工具」捷徑,請務必前往設定啟用「在螢幕鎖定畫面上顯示小工具」。"</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"設定"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"顯示螢幕保護程式按鈕"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會刪除。"</string>
@@ -750,8 +753,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"更新中"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"工作資料夾"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"飛航模式"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"家長監護"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"你不會聽到下一個<xliff:g id="WHEN">%1$s</xliff:g> 的鬧鐘"</string>
     <string name="alarm_template" msgid="2234991538018805736">"於<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"於<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -802,6 +804,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"以對話框的形式顯示在對話通知頂端 (螢幕鎖定時會顯示為個人資料相片),並會中斷「零打擾」模式"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"優先"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」不支援對話功能"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"無法修改這些通知。"</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"無法修改來電通知。"</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"無法在這裡設定這個通知群組"</string>
@@ -912,6 +918,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"日曆"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"計算機"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"地圖"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"零打擾"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"音量鍵快速鍵"</string>
     <string name="battery" msgid="769686279459897127">"電池"</string>
@@ -1432,8 +1448,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系統應用程式"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割畫面"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"無障礙功能"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"輸入"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"應用程式捷徑"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"目前的應用程式"</string>
@@ -1497,11 +1512,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"你已完成「查看最近使用的應用程式」手勢教學課程。"</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"如要查看最近使用的應用程式,請在觸控板上用三指向上滑動並按住"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"切換應用程式"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"太棒了!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"你已完成應用程式切換手勢的教學課程。"</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有應用程式"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index a210ca3..16d223d 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -537,10 +537,13 @@
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Amawijethi wesikrini esikhiyiwe"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ukuze uvule i-app usebenzisa iwijethi, uzodinga ukuqinisekisa ukuthi nguwe. Futhi, khumbula ukuthi noma ubani angakwazi ukuzibuka, nanoma ithebhulethi yakho ikhiyiwe. Amanye amawijethi kungenzeka abengahloselwe ukukhiya isikrini sakho futhi kungenzeka awaphephile ukuthi angafakwa lapha."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ngiyezwa"</string>
-    <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Amawijethi"</string>
-    <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Ukuze ufake isinqamuleli esithi \"Amawijethi\", qinisekisa ukuthi okuthi \"Bonisa amawijethi esikrinini sokukhiya\" kunikwe amandla kumasethingi."</string>
-    <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Amasethingi"</string>
     <string name="accessibility_glanceable_hub_to_dream_button" msgid="7552776300297055307">"Bonisa inkinobho yesigcini sesikrini"</string>
+    <!-- no translation found for hub_onboarding_bottom_sheet_title (162092881395529947) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_text (8589816797970240544) -->
+    <skip />
+    <!-- no translation found for hub_onboarding_bottom_sheet_action_button (6161983690157872829) -->
+    <skip />
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"imenyu yokudonsela phansi"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wonke ama-app nedatha kulesi sikhathi azosuswa."</string>
@@ -752,8 +755,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Iyabuyekeza"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Iphrofayela yomsebenzi"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Imodi yendiza"</string>
-    <!-- no translation found for status_bar_supervision (6735015942701134125) -->
-    <skip />
+    <string name="status_bar_supervision" msgid="6735015942701134125">"Izilawuli zomzali"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"Ngeke uzwe i-alamu yakho elandelayo ngo-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"ngo-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"nge-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -804,6 +806,10 @@
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Ivela phezu kwezaziso zengxoxo futhi njengesithombe sephrofayela esikrinini sokukhiya, ivela njengebhamuza, ukuphazamisa okuthi Ungaphazamisi"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Okubalulekile"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayisekeli izici zengxoxo"</string>
+    <!-- no translation found for notification_inline_dismiss (88423586921134258) -->
+    <skip />
+    <!-- no translation found for notification_inline_disable_promotion (6880961831026048166) -->
+    <skip />
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Lezi zaziso azikwazi ukushintshwa."</string>
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Izaziso zekholi azikwazi ukushintshwa."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Leli qembu lezaziso alikwazi ukulungiselelwa lapha"</string>
@@ -914,6 +920,16 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Ikhalenda"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Isibali"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"I-Maps"</string>
+    <!-- no translation found for group_accessibility_toggle_bounce_keys (4183584952493519179) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_mouse_keys (534757719357514361) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_sticky_keys (7722214637652104184) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_slow_keys (8569881436531795062) -->
+    <skip />
+    <!-- no translation found for group_accessibility_toggle_voice_access (5436708239015479017) -->
+    <skip />
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ungaphazamisi"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"Izinqamuleli zezinkinobho zevolomu"</string>
     <string name="battery" msgid="769686279459897127">"Ibhethri"</string>
@@ -1434,8 +1450,7 @@
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Ama-app esistimu"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Ukwenza imisebenzi eminingi"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Hlukanisa isikrini"</string>
-    <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
-    <skip />
+    <string name="shortcutHelper_category_accessibility" msgid="8068337792277570938">"Ukufinyeleleka"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Okokufaka"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Izinqamuleli Zohlelo lokusebenza"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"I-App yamanje"</string>
@@ -1499,11 +1514,11 @@
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Uqedele ukubuka ukuthinta kwama-app akamuva."</string>
     <string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Ukuze ubuke ama-app akamuva, swayiphela phezulu futhi ubambe usebenzisa iminwe emithathu kuphedi yakho yokuthinta"</string>
     <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Shintsha ama-app"</string>
-    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
+    <!-- no translation found for touchpad_switch_apps_gesture_guidance (2554844933805502538) -->
     <skip />
     <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Umsebenzi omuhle!"</string>
     <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ukuqedile ukuthinta kokushintsha ama-app."</string>
-    <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
+    <!-- no translation found for touchpad_switch_gesture_error_body (5895231916964677918) -->
     <skip />
     <string name="tutorial_action_key_title" msgid="8172535792469008169">"Buka wonke ama-app"</string>
     <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Cindezela inkinobho yokufinyelela kukhibhodi yakho"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 36ede64..015e0e8 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -24,6 +24,7 @@
     <color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
     <color name="qs_detail_button_white">#B3FFFFFF</color><!-- 70% white -->
     <color name="status_bar_clock_color">#FFFFFFFF</color>
+    <color name="shade_header_text_color">#FFFFFFFF</color>
     <color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
     <color name="status_bar_icons_hover_color_light">#38FFFFFF</color> <!-- 22% white -->
     <color name="status_bar_icons_hover_color_dark">#38000000</color> <!-- 22% black -->
@@ -260,4 +261,8 @@
     <!-- Rear Display Education -->
     <color name="rear_display_overlay_animation_background_color">#1E1B17</color>
     <color name="rear_display_overlay_dialog_background_color">#1E1B17</color>
+
+    <!-- Low light Dream -->
+    <color name="low_light_clock_background_color">#000000</color>
+    <color name="low_light_clock_text_color">#CCCCCC</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 940e87d..68e33f2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -1104,7 +1104,10 @@
     -->
     <bool name="config_userSwitchingMustGoThroughLoginScreen">false</bool>
 
-
     <!-- The dream component used when the device is low light environment. -->
     <string translatable="false" name="config_lowLightDreamComponent"/>
+
+    <!--Whether we should position magic portrait shape effects in the center of lockscreen
+    it's false by default, and only be true in tablet landscape -->
+    <bool name="center_align_magic_portrait_shape">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8a0ffb9..42d66e2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -266,6 +266,9 @@
     <!-- Height of a large notification in the status bar -->
     <dimen name="notification_max_height">358dp</dimen>
 
+    <!-- Height of a large promoted ongoing notification in the status bar -->
+    <dimen name="notification_max_height_for_promoted_ongoing">272dp</dimen>
+
     <!-- Height of a heads up notification in the status bar for legacy custom views -->
     <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
 
@@ -1611,6 +1614,18 @@
     <!-- GLANCEABLE_HUB -> DREAMING transition: Amount to shift dream overlay on entering -->
     <dimen name="hub_to_dreaming_transition_dream_overlay_translation_x">824dp</dimen>
 
+    <!-- Low light clock -->
+    <!-- The text size of the low light clock is intentionally defined in dp to avoid scaling -->
+    <dimen name="low_light_clock_text_size">260dp</dimen>
+    <dimen name="low_light_clock_charging_text_size">14sp</dimen>
+    <dimen name="low_light_clock_charging_text_min_height">48dp</dimen>
+    <integer name="low_light_clock_charging_text_font_weight">500</integer>
+
+    <dimen name="low_light_clock_translate_animation_offset">40dp</dimen>
+    <integer name="low_light_clock_translate_animation_duration_ms">1167</integer>
+    <integer name="low_light_clock_alpha_animation_in_start_delay_ms">233</integer>
+    <integer name="low_light_clock_alpha_animation_duration_ms">250</integer>
+
     <!-- Distance that the full shade transition takes in order for media to fully transition to
          the shade -->
     <dimen name="lockscreen_shade_media_transition_distance">120dp</dimen>
@@ -2135,4 +2150,8 @@
     <dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
     <dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
     <!-- Volume end -->
+
+    <!-- Gradient color wallpaper start -->
+    <dimen name="gradient_color_wallpaper_center_offset">128dp</dimen>
+    <!-- Gradient color wallpaper end -->
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1e217de..fc9635b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -588,6 +588,12 @@
     <!-- Content description of the cast label showing what we are connected to. [CHAR LIMIT=NONE] -->
     <string name="accessibility_cast_name">Connected to <xliff:g id="cast" example="TV">%s</xliff:g>.</string>
 
+    <!-- Content description of the button to expand the group of devices. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_expand_group">Expand group.</string>
+
+    <!-- Content description of the button to open the application . [CHAR LIMIT=NONE] -->
+    <string name="accessibility_open_application">Open application.</string>
+
     <!-- Content description of an item with no signal and no connection for accessibility (not shown on the screen) [CHAR LIMIT=NONE] -->
     <string name="accessibility_not_connected">Not connected.</string>
     <!-- Content description of the roaming data connection type. [CHAR LIMIT=NONE] -->
@@ -1359,6 +1365,8 @@
     <string name="hub_onboarding_bottom_sheet_text">Access your favorite widgets and screen savers while charging.</string>
     <!-- Hub onboarding bottom sheet action button title. [CHAR LIMIT=NONE] -->
     <string name="hub_onboarding_bottom_sheet_action_button">Let\u2019s go</string>
+    <!-- Text for a tooltip that appears over the "show screensaver" button on glanceable hub. [CHAR LIMIT=NONE] -->
+    <string name="glanceable_hub_to_dream_button_tooltip">Show your favorite screensavers while charging</string>
 
     <!-- Related to user switcher --><skip/>
 
@@ -2083,6 +2091,12 @@
     <!-- [CHAR LIMIT=80] Text shown in feedback button in notification guts for a bundled notification -->
     <string name="notification_guts_bundle_feedback" translatable="false">Provide Bundle Feedback</string>
 
+    <!-- [CHAR LIMIT=30] Text shown in button used to dismiss this single notification. -->
+    <string name="notification_inline_dismiss">Dismiss</string>
+
+    <!-- [CHAR LIMIT=30] Text shown in button used to prevent app from showing Live Updates, for this notification and all future ones -->
+    <string name="notification_inline_disable_promotion">Don\'t show again</string>
+
     <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
     <string name="notification_unblockable_desc">These notifications can\'t be modified.</string>
 
@@ -2310,9 +2324,6 @@
     <string name="group_system_lock_screen">Lock screen</string>
     <!-- User visible title for the keyboard shortcut that pulls up Notes app for quick memo. [CHAR LIMIT=70] -->
     <string name="group_system_quick_memo">Take a note</string>
-    <!-- TODO(b/383734125): make it translatable once string is finalized by UXW.-->
-    <!-- User visible title for the keyboard shortcut that toggles Voice Access. [CHAR LIMIT=70] -->
-    <string name="group_system_toggle_voice_access" translatable="false">Toggle Voice Access</string>
 
     <!-- User visible title for the multitasking keyboard shortcuts list. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_system_multitasking">Multitasking</string>
@@ -2371,6 +2382,23 @@
     <!-- User visible title for the keyboard shortcut that takes the user to the maps app. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications_maps">Maps</string>
 
+    <!-- User visible title for the keyboard shortcut that toggles bounce keys. [CHAR LIMIT=70]-->
+    <string name="group_accessibility_toggle_bounce_keys">Toggle bounce keys</string>
+    <!-- User visible title for the keyboard shortcut that toggles mouse keys. [CHAR LIMIT=70]-->
+    <string name="group_accessibility_toggle_mouse_keys">Toggle mouse keys</string>
+    <!-- User visible title for the keyboard shortcut that toggles sticky keys. [CHAR LIMIT=70]-->
+    <string name="group_accessibility_toggle_sticky_keys">Toggle sticky keys</string>
+    <!-- User visible title for the keyboard shortcut that toggles slow keys. [CHAR LIMIT=70]-->
+    <string name="group_accessibility_toggle_slow_keys">Toggle slow keys</string>
+    <!-- User visible title for the keyboard shortcut that toggles Voice Access. [CHAR LIMIT=70] -->
+    <string name="group_accessibility_toggle_voice_access">Toggle Voice Access</string>
+    <!-- User visible title for the keyboard shortcut that toggles Talkback. [CHAR LIMIT=70] -->
+    <string name="group_accessibility_toggle_talkback">Toggle Talkback</string>
+    <!-- User visible title for the keyboard shortcut that toggles Magnification. [CHAR LIMIT=70] -->
+    <string name="group_accessibility_toggle_magnification">Toggle Magnification</string>
+    <!-- User visible title for the keyboard shortcut that activates Select to Speak service. [CHAR LIMIT=70] -->
+    <string name="group_accessibility_activate_select_to_speak">Activate Select to Speak</string>
+
     <!-- SysUI Tuner: Label for screen about do not disturb settings [CHAR LIMIT=60] -->
     <string name="volume_and_do_not_disturb">Do Not Disturb</string>
 
@@ -4005,13 +4033,13 @@
     <!-- Touchpad switch apps gesture action name in tutorial [CHAR LIMIT=NONE] -->
     <string name="touchpad_switch_apps_gesture_action_title">Switch apps</string>
     <!-- Touchpad switch apps gesture guidance in gestures tutorial [CHAR LIMIT=NONE] -->
-    <string name="touchpad_switch_apps_gesture_guidance">Swipe left using four fingers on your touchpad</string>
+    <string name="touchpad_switch_apps_gesture_guidance">Swipe right using four fingers on your touchpad</string>
     <!-- Screen title after switch apps gesture was done successfully [CHAR LIMIT=NONE] -->
     <string name="touchpad_switch_apps_gesture_success_title">Great job!</string>
     <!-- Text shown to the user after they complete switch apps gesture tutorial [CHAR LIMIT=NONE] -->
     <string name="touchpad_switch_apps_gesture_success_body">You completed the switch apps gesture.</string>
     <!-- Text shown to the user after switch gesture was not done correctly [CHAR LIMIT=NONE] -->
-    <string name="touchpad_switch_gesture_error_body">Swipe left using four fingers on your touchpad to switch apps</string>
+    <string name="touchpad_switch_gesture_error_body">Swipe right using four fingers on your touchpad to switch apps</string>
 
     <!-- KEYBOARD TUTORIAL-->
     <!-- Action key tutorial title [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b0d9bed..fa6a41a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -176,17 +176,11 @@
 
     <style name="TextAppearance.QS.Status">
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
-        <item name="android:textColor">?attr/onSurface</item>
+        <item name="android:textColor">@color/shade_header_text_color</item>
         <item name="android:textSize">14sp</item>
         <item name="android:letterSpacing">0.01</item>
     </style>
 
-    <style name="TextAppearance.QS.Status.Carriers" />
-
-    <style name="TextAppearance.QS.Status.Carriers.NoCarrierText">
-        <item name="android:textColor">?attr/onSurfaceVariant</item>
-    </style>
-
     <style name="TextAppearance.QS.Status.Build">
         <item name="android:textColor">?attr/onSurfaceVariant</item>
     </style>
@@ -565,16 +559,6 @@
         <item name="android:windowNoTitle">true</item>
     </style>
 
-    <style name="SystemUI.Material3.Slider.Volume">
-        <item name="trackHeight">40dp</item>
-        <item name="thumbHeight">52dp</item>
-        <item name="trackCornerSize">12dp</item>
-        <item name="trackInsideCornerSize">2dp</item>
-        <item name="trackStopIndicatorSize">6dp</item>
-        <item name="trackIconSize">20dp</item>
-        <item name="labelBehavior">gone</item>
-    </style>
-
     <style name="SystemUI.Material3.Slider" parent="@style/Widget.Material3.Slider">
         <item name="labelStyle">@style/Widget.Material3.Slider.Label</item>
         <item name="thumbColor">@color/thumb_color</item>
diff --git a/packages/SystemUI/res/xml/gradient_color_wallpaper.xml b/packages/SystemUI/res/xml/gradient_color_wallpaper.xml
new file mode 100644
index 0000000..f453a44
--- /dev/null
+++ b/packages/SystemUI/res/xml/gradient_color_wallpaper.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (C) 2025 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
+  -->
+<wallpaper
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:showMetadataInPreview="false"
+    android:supportsMultipleDisplays="true" />
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
similarity index 91%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
rename to packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
index d363e52..10b9303 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
@@ -23,8 +23,8 @@
 import android.view.MotionEvent;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 
-// Next ID: 38
-oneway interface IOverviewProxy {
+// Next ID: 39
+oneway interface ILauncherProxy {
 
     void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
 
@@ -140,18 +140,23 @@
     void appTransitionPending(boolean pending) = 34;
 
     /**
-     * Sent right after OverviewProxy calls unbindService() on the TouchInteractionService.
+     * Sent right after LauncherProxyService calls unbindService() on the TouchInteractionService.
      * TouchInteractionService is expected to send the reply once it has finished cleaning up.
      */
     void onUnbind(IRemoteCallback reply) = 35;
 
     /**
-     * Sent when {@link TaskbarDelegate#onDisplayReady} is called.
+     * Sent when {@link TaskbarDelegate#onDisplayAddSystemDecorations} is called.
      */
-    void onDisplayReady(int displayId) = 36;
+    void onDisplayAddSystemDecorations(int displayId) = 36;
 
     /**
      * Sent when {@link TaskbarDelegate#onDisplayRemoved} is called.
      */
     void onDisplayRemoved(int displayId) = 37;
+
+    /**
+     * Sent when {@link TaskbarDelegate#onDisplayRemoveSystemDecorations} is called.
+     */
+    void onDisplayRemoveSystemDecorations(int displayId) = 38;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index e332280..1f6bea1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -69,10 +69,10 @@
 
     /**
      * Indicates that the given Assist invocation types should be handled by Launcher via
-     * OverviewProxy#onAssistantOverrideInvoked and should not be invoked by SystemUI.
+     * LauncherProxy#onAssistantOverrideInvoked and should not be invoked by SystemUI.
      *
      * @param invocationTypes The invocation types that will henceforth be handled via
-     *         OverviewProxy (Launcher); other invocation types should be handled by SysUI.
+     *         LauncherProxy (Launcher); other invocation types should be handled by SysUI.
      */
     oneway void setAssistantOverridesRequested(in int[] invocationTypes) = 53;
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 8576a6e..a518c57 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -72,6 +72,25 @@
         @ViewDebug.ExportedProperty(category = "recents")
         public final int displayId;
 
+        /**
+         * The component of the first activity in the task, can be considered the "application" of
+         * this task.
+         */
+        @Nullable
+        public ComponentName baseActivity;
+        /**
+         * The number of activities in this task (including running).
+         */
+        public int numActivities;
+        /**
+         * Whether the top activity is to be displayed. See {@link android.R.attr#windowNoDisplay}.
+         */
+        public boolean isTopActivityNoDisplay;
+        /**
+         * Whether fillsParent() is false for every activity in the tasks stack.
+         */
+        public boolean isActivityStackTransparent;
+
         // The source component name which started this task
         public final ComponentName sourceComponent;
 
@@ -90,6 +109,10 @@
             this.userId = t.userId;
             this.lastActiveTime = t.lastActiveTime;
             this.displayId = t.displayId;
+            this.baseActivity = t.baseActivity;
+            this.numActivities = t.numActivities;
+            this.isTopActivityNoDisplay = t.isTopActivityNoDisplay;
+            this.isActivityStackTransparent = t.isActivityStackTransparent;
             updateHashCode();
         }
 
@@ -106,7 +129,9 @@
         }
 
         public TaskKey(int id, int windowingMode, @NonNull Intent intent,
-                ComponentName sourceComponent, int userId, long lastActiveTime, int displayId) {
+                ComponentName sourceComponent, int userId, long lastActiveTime, int displayId,
+                @Nullable ComponentName baseActivity, int numActivities,
+                boolean isTopActivityNoDisplay, boolean isActivityStackTransparent) {
             this.id = id;
             this.windowingMode = windowingMode;
             this.baseIntent = intent;
@@ -114,6 +139,10 @@
             this.userId = userId;
             this.lastActiveTime = lastActiveTime;
             this.displayId = displayId;
+            this.baseActivity = baseActivity;
+            this.numActivities = numActivities;
+            this.isTopActivityNoDisplay = isTopActivityNoDisplay;
+            this.isActivityStackTransparent = isActivityStackTransparent;
             updateHashCode();
         }
 
@@ -185,6 +214,10 @@
             parcel.writeLong(lastActiveTime);
             parcel.writeInt(displayId);
             parcel.writeTypedObject(sourceComponent, flags);
+            parcel.writeTypedObject(baseActivity, flags);
+            parcel.writeInt(numActivities);
+            parcel.writeBoolean(isTopActivityNoDisplay);
+            parcel.writeBoolean(isActivityStackTransparent);
         }
 
         private static TaskKey readFromParcel(Parcel parcel) {
@@ -195,9 +228,14 @@
             long lastActiveTime = parcel.readLong();
             int displayId = parcel.readInt();
             ComponentName sourceComponent = parcel.readTypedObject(ComponentName.CREATOR);
+            ComponentName baseActivity = parcel.readTypedObject(ComponentName.CREATOR);
+            int numActivities = parcel.readInt();
+            boolean isTopActivityNoDisplay = parcel.readBoolean();
+            boolean isActivityStackTransparent = parcel.readBoolean();
 
             return new TaskKey(id, windowingMode, baseIntent, sourceComponent, userId,
-                    lastActiveTime, displayId);
+                    lastActiveTime, displayId, baseActivity, numActivities, isTopActivityNoDisplay,
+                    isActivityStackTransparent);
         }
 
         @Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 41ad437..9ebb15f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -16,11 +16,12 @@
 
 package com.android.systemui.shared.recents.utilities;
 
-import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.app.StatusBarManager.NAVBAR_BACK_DISMISS_IME;
+import static android.app.StatusBarManager.NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
+import static android.app.StatusBarManager.NAVBAR_IME_VISIBLE;
 
 import android.annotation.TargetApi;
+import android.app.StatusBarManager.NavbarFlags;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
@@ -103,38 +104,46 @@
     }
 
     /**
-     * @return updated set of flags from InputMethodService based off {@param oldHints}
-     *          Leaves original hints unmodified
+     * Updates the navigation bar state flags with the given IME state.
+     *
+     * @param oldFlags        current navigation bar state flags.
+     * @param backDisposition the IME back disposition mode. Only takes effect if
+     *                        {@code isImeVisible} is {@code true}.
+     * @param isImeVisible    whether the IME is currently visible.
+     * @param showImeSwitcher whether the IME Switcher button should be shown. Only takes effect if
+     *                        {@code isImeVisible} is {@code true}.
      */
-    public static int calculateBackDispositionHints(int oldHints,
-            @BackDispositionMode int backDisposition, boolean imeShown, boolean showImeSwitcher) {
-        int hints = oldHints;
+    @NavbarFlags
+    public static int updateNavbarFlagsFromIme(@NavbarFlags int oldFlags,
+            @BackDispositionMode int backDisposition, boolean isImeVisible,
+            boolean showImeSwitcher) {
+        int flags = oldFlags;
         switch (backDisposition) {
             case InputMethodService.BACK_DISPOSITION_DEFAULT:
             case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
             case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
-                if (imeShown) {
-                    hints |= NAVIGATION_HINT_BACK_ALT;
+                if (isImeVisible) {
+                    flags |= NAVBAR_BACK_DISMISS_IME;
                 } else {
-                    hints &= ~NAVIGATION_HINT_BACK_ALT;
+                    flags &= ~NAVBAR_BACK_DISMISS_IME;
                 }
                 break;
             case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
-                hints &= ~NAVIGATION_HINT_BACK_ALT;
+                flags &= ~NAVBAR_BACK_DISMISS_IME;
                 break;
         }
-        if (imeShown) {
-            hints |= NAVIGATION_HINT_IME_SHOWN;
+        if (isImeVisible) {
+            flags |= NAVBAR_IME_VISIBLE;
         } else {
-            hints &= ~NAVIGATION_HINT_IME_SHOWN;
+            flags &= ~NAVBAR_IME_VISIBLE;
         }
-        if (showImeSwitcher) {
-            hints |= NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+        if (showImeSwitcher && isImeVisible) {
+            flags |= NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
         } else {
-            hints &= ~NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+            flags &= ~NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
         }
 
-        return hints;
+        return flags;
     }
 
     /** @return whether or not {@param context} represents that of a large screen device or not */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
index bd20777..e928525a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.shared.shadow
 
 import android.content.Context
+import android.content.res.TypedArray
 import android.graphics.Canvas
 import android.graphics.drawable.Drawable
 import android.util.AttributeSet
@@ -31,19 +32,23 @@
     context: Context,
     attrs: AttributeSet? = null,
     defStyleAttr: Int = 0,
-    defStyleRes: Int = 0
+    defStyleRes: Int = 0,
 ) : TextView(context, attrs, defStyleAttr, defStyleRes) {
-    private val mKeyShadowInfo: ShadowInfo
-    private val mAmbientShadowInfo: ShadowInfo
+    private lateinit var mKeyShadowInfo: ShadowInfo
+    private lateinit var mAmbientShadowInfo: ShadowInfo
 
     init {
-        val attributes =
+        updateShadowDrawables(
             context.obtainStyledAttributes(
                 attrs,
                 R.styleable.DoubleShadowTextView,
                 defStyleAttr,
-                defStyleRes
+                defStyleRes,
             )
+        )
+    }
+
+    private fun updateShadowDrawables(attributes: TypedArray) {
         val drawableSize: Int
         val drawableInsetSize: Int
         try {
@@ -70,17 +75,17 @@
                     ambientShadowBlur,
                     ambientShadowOffsetX,
                     ambientShadowOffsetY,
-                    ambientShadowAlpha
+                    ambientShadowAlpha,
                 )
             drawableSize =
                 attributes.getDimensionPixelSize(
                     R.styleable.DoubleShadowTextView_drawableIconSize,
-                    0
+                    0,
                 )
             drawableInsetSize =
                 attributes.getDimensionPixelSize(
                     R.styleable.DoubleShadowTextView_drawableIconInsetSize,
-                    0
+                    0,
                 )
         } finally {
             attributes.recycle()
@@ -95,12 +100,19 @@
                     mAmbientShadowInfo,
                     drawable,
                     drawableSize,
-                    drawableInsetSize
+                    drawableInsetSize,
                 )
         }
         setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3])
     }
 
+    override fun setTextAppearance(resId: Int) {
+        super.setTextAppearance(resId)
+        updateShadowDrawables(
+            context.obtainStyledAttributes(resId, R.styleable.DoubleShadowTextView)
+        )
+    }
+
     public override fun onDraw(canvas: Canvas) {
         applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) }
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 6f13d63..82ac78c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -98,12 +98,12 @@
     public static final long SYSUI_STATE_ONE_HANDED_ACTIVE = 1L << 16;
     // Allow system gesture no matter the system bar(s) is visible or not
     public static final long SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1L << 17;
-    // The IME is showing
-    public static final long SYSUI_STATE_IME_SHOWING = 1L << 18;
+    // The IME is visible.
+    public static final long SYSUI_STATE_IME_VISIBLE = 1L << 18;
     // The window magnification is overlapped with system gesture insets at the bottom.
     public static final long SYSUI_STATE_MAGNIFICATION_OVERLAP = 1L << 19;
-    // ImeSwitcher is showing
-    public static final long SYSUI_STATE_IME_SWITCHER_SHOWING = 1L << 20;
+    // The IME Switcher button is visible.
+    public static final long SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE = 1L << 20;
     // Device dozing/AOD state
     public static final long SYSUI_STATE_DEVICE_DOZING = 1L << 21;
     // The home feature is disabled (either by SUW/SysUI/device policy)
@@ -134,6 +134,10 @@
     public static final long SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING = 1L << 34;
     // Communal hub is showing
     public static final long SYSUI_STATE_COMMUNAL_HUB_SHOWING = 1L << 35;
+    // The back button is visually adjusted to indicate that it will dismiss the IME when pressed.
+    // This only takes effect while the IME is visible. By default, it is set while the IME is
+    // visible, but may be overridden by the backDispositionMode set by the IME.
+    public static final long SYSUI_STATE_BACK_DISMISS_IME = 1L << 36;
 
     // Mask for SystemUiStateFlags to isolate SYSUI_STATE_AWAKE and
     // SYSUI_STATE_WAKEFULNESS_TRANSITION, to match WAKEFULNESS_* constants
@@ -168,9 +172,9 @@
             SYSUI_STATE_DIALOG_SHOWING,
             SYSUI_STATE_ONE_HANDED_ACTIVE,
             SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
-            SYSUI_STATE_IME_SHOWING,
+            SYSUI_STATE_IME_VISIBLE,
             SYSUI_STATE_MAGNIFICATION_OVERLAP,
-            SYSUI_STATE_IME_SWITCHER_SHOWING,
+            SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE,
             SYSUI_STATE_DEVICE_DOZING,
             SYSUI_STATE_BACK_DISABLED,
             SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED,
@@ -185,6 +189,7 @@
             SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED,
             SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING,
             SYSUI_STATE_COMMUNAL_HUB_SHOWING,
+            SYSUI_STATE_BACK_DISMISS_IME,
     })
     public @interface SystemUiStateFlags {}
 
@@ -244,14 +249,14 @@
         if ((flags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
             str.add("allow_gesture");
         }
-        if ((flags & SYSUI_STATE_IME_SHOWING) != 0) {
+        if ((flags & SYSUI_STATE_IME_VISIBLE) != 0) {
             str.add("ime_visible");
         }
         if ((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0) {
             str.add("magnification_overlap");
         }
-        if ((flags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0) {
-            str.add("ime_switcher_showing");
+        if ((flags & SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE) != 0) {
+            str.add("ime_switcher_button_visible");
         }
         if ((flags & SYSUI_STATE_DEVICE_DOZING) != 0) {
             str.add("device_dozing");
@@ -295,6 +300,9 @@
         if ((flags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) {
             str.add("communal_hub_showing");
         }
+        if ((flags & SYSUI_STATE_BACK_DISMISS_IME) != 0) {
+            str.add("back_dismiss_ime");
+        }
 
         return str.toString();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierText.java b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
index ae282c7..e2ac6a4 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierText.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
@@ -22,6 +22,7 @@
 import android.text.method.SingleLineTransformationMethod;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.TextView;
 
 import com.android.systemui.res.R;
@@ -65,6 +66,14 @@
         }
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        // Clear selected state set by CarrierTextController so "selected" not announced by
+        // accessibility but we can still marquee.
+        info.setSelected(false);
+    }
+
     public boolean getShowAirplaneMode() {
         return mShowAirplaneMode;
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 7f176de..0e9d8fe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard;
 
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+
 import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
@@ -164,6 +166,8 @@
             layoutParams.height = (int) getResources().getDimension(
                     R.dimen.keyguard_pin_field_height);
         }
+
+        mPasswordEntry.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
     }
 
     private void setKeyboardBasedFocusOutline(boolean isAnyKeyboardConnected) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 641cac5..22092dcd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -111,7 +111,9 @@
 
         // Prevent multiple inflations for the same security mode. Instead, add callback to a list
         // and then notify each in order when the view is inflated.
-        mOnViewInflatedListeners.add(onViewInflatedCallback);
+        synchronized (mOnViewInflatedListeners) {
+            mOnViewInflatedListeners.add(onViewInflatedCallback);
+        }
         if (!mSecurityModeInProgress.contains(securityMode)) {
             mSecurityModeInProgress.add(securityMode);
             asynchronouslyInflateView(securityMode, keyguardSecurityCallback);
@@ -145,10 +147,14 @@
                         childController.init();
                         mChildren.add(childController);
 
-                        for (OnViewInflatedCallback callback : mOnViewInflatedListeners) {
+                        List<OnViewInflatedCallback> callbacks;
+                        synchronized (mOnViewInflatedListeners) {
+                            callbacks = new ArrayList<>(mOnViewInflatedListeners);
+                            mOnViewInflatedListeners.clear();
+                        }
+                        for (OnViewInflatedCallback callback : callbacks) {
                             callback.onViewInflated(childController);
                         }
-                        mOnViewInflatedListeners.clear();
 
                         // Single bouncer constrains are default
                         if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) {
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index e76f38c..9507b04 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -62,15 +62,14 @@
                 scope,
                 mainDispatcher,
                 bgDispatcher,
-                com.android.systemui.Flags.lockscreenCustomClocks()
+                com.android.systemui.shared.Flags.lockscreenCustomClocks()
                         || featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
                 /* handleAllUsers= */ true,
                 new DefaultClockProvider(
                         context,
                         layoutInflater,
                         resources,
-
-                        com.android.systemui.Flags.clockReactiveVariants()
+                        com.android.systemui.shared.Flags.clockReactiveVariants()
                 ),
                 context.getString(R.string.lockscreen_clock_id_fallback),
                 clockBuffers,
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 40c1f0f9..4a8e4ed 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -39,7 +39,7 @@
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -129,7 +129,7 @@
     @Inject Lazy<MetricsLogger> mMetricsLogger;
     @Inject Lazy<UiOffloadThread> mUiOffloadThread;
     @Inject Lazy<LightBarController> mLightBarController;
-    @Inject Lazy<OverviewProxyService> mOverviewProxyService;
+    @Inject Lazy<LauncherProxyService> mLauncherProxyService;
     @Inject Lazy<NavigationModeController> mNavBarModeController;
     @Inject Lazy<NavigationBarController> mNavigationBarController;
     @Inject Lazy<StatusBarStateController> mStatusBarStateController;
@@ -175,7 +175,7 @@
         mProviders.put(MetricsLogger.class, mMetricsLogger::get);
         mProviders.put(UiOffloadThread.class, mUiOffloadThread::get);
         mProviders.put(LightBarController.class, mLightBarController::get);
-        mProviders.put(OverviewProxyService.class, mOverviewProxyService::get);
+        mProviders.put(LauncherProxyService.class, mLauncherProxyService::get);
         mProviders.put(NavigationModeController.class, mNavBarModeController::get);
         mProviders.put(NavigationBarController.class, mNavigationBarController::get);
         mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
diff --git a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
index 11ce168..dbdf93d 100644
--- a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
@@ -109,42 +109,34 @@
             requestLayout()
         }
 
-        cameraProtectionAnimator?.cancel()
-        cameraProtectionAnimator = ValueAnimator.ofFloat(cameraProtectionProgress,
-                if (showScanningAnimationNow) SHOW_CAMERA_PROTECTION_SCALE
-                else HIDDEN_CAMERA_PROTECTION_SCALE).apply {
-            startDelay =
-                    if (showScanningAnim) 0
-                    else if (faceAuthSucceeded) PULSE_SUCCESS_DISAPPEAR_DURATION
-                    else PULSE_ERROR_DISAPPEAR_DURATION
-            duration =
-                    if (showScanningAnim) CAMERA_PROTECTION_APPEAR_DURATION
-                    else if (faceAuthSucceeded) CAMERA_PROTECTION_SUCCESS_DISAPPEAR_DURATION
-                    else CAMERA_PROTECTION_ERROR_DISAPPEAR_DURATION
-            interpolator =
-                    if (showScanningAnim) Interpolators.STANDARD_ACCELERATE
-                    else if (faceAuthSucceeded) Interpolators.STANDARD
-                    else Interpolators.STANDARD_DECELERATE
-            addUpdateListener(this@FaceScanningOverlay::updateCameraProtectionProgress)
-            addListener(object : AnimatorListenerAdapter() {
-                override fun onAnimationEnd(animation: Animator) {
-                    cameraProtectionAnimator = null
-                    if (!showScanningAnim) {
-                        hide()
-                    }
-                }
-            })
+        if (!Flags.faceScanningAnimationNpeFix()) {
+            cameraProtectionAnimator?.cancel()
+            cameraProtectionAnimator = cameraProtectionAnimator(faceAuthSucceeded)
         }
 
         rimAnimator?.cancel()
-        rimAnimator = if (showScanningAnim) {
-            createFaceScanningRimAnimator()
+        rimAnimator = faceScanningRimAnimator(
+            faceAuthSucceeded,
+            if (Flags.faceScanningAnimationNpeFix()) {
+                cameraProtectionAnimator(faceAuthSucceeded)
+            } else {
+                cameraProtectionAnimator
+            },
+        )
+        rimAnimator?.start()
+    }
+
+    private fun faceScanningRimAnimator(
+        faceAuthSucceeded: Boolean,
+        cameraProtectAnimator: ValueAnimator?
+    ): AnimatorSet {
+        return if (showScanningAnim) {
+            createFaceScanningRimAnimator(cameraProtectAnimator)
         } else if (faceAuthSucceeded) {
-            createFaceSuccessRimAnimator()
+            createFaceSuccessRimAnimator(cameraProtectAnimator)
         } else {
-            createFaceNotSuccessRimAnimator()
-        }
-        rimAnimator?.apply {
+            createFaceNotSuccessRimAnimator(cameraProtectAnimator)
+        }.apply {
             addListener(object : AnimatorListenerAdapter() {
                 override fun onAnimationEnd(animation: Animator) {
                     rimAnimator = null
@@ -154,7 +146,38 @@
                 }
             })
         }
-        rimAnimator?.start()
+    }
+
+    private fun cameraProtectionAnimator(faceAuthSucceeded: Boolean): ValueAnimator {
+        return ValueAnimator.ofFloat(
+            cameraProtectionProgress,
+            if (showScanningAnim) SHOW_CAMERA_PROTECTION_SCALE
+            else HIDDEN_CAMERA_PROTECTION_SCALE
+        ).apply {
+            startDelay =
+                if (showScanningAnim) 0
+                else if (faceAuthSucceeded) PULSE_SUCCESS_DISAPPEAR_DURATION
+                else PULSE_ERROR_DISAPPEAR_DURATION
+            duration =
+                if (showScanningAnim) CAMERA_PROTECTION_APPEAR_DURATION
+                else if (faceAuthSucceeded) CAMERA_PROTECTION_SUCCESS_DISAPPEAR_DURATION
+                else CAMERA_PROTECTION_ERROR_DISAPPEAR_DURATION
+            interpolator =
+                if (showScanningAnim) Interpolators.STANDARD_ACCELERATE
+                else if (faceAuthSucceeded) Interpolators.STANDARD
+                else Interpolators.STANDARD_DECELERATE
+            addUpdateListener(this@FaceScanningOverlay::updateCameraProtectionProgress)
+            addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator) {
+                    if (!Flags.faceScanningAnimationNpeFix()) {
+                        cameraProtectionAnimator = null
+                    }
+                    if (!showScanningAnim) {
+                        hide()
+                    }
+                }
+            })
+        }
     }
 
     override fun updateVisOnUpdateCutout(): Boolean {
@@ -219,7 +242,7 @@
         canvas.drawPath(scaledProtectionPath, paint)
     }
 
-    private fun createFaceSuccessRimAnimator(): AnimatorSet {
+    private fun createFaceSuccessRimAnimator(cameraProtectAnimator: ValueAnimator?): AnimatorSet {
         val rimSuccessAnimator = AnimatorSet()
         rimSuccessAnimator.playTogether(
             createRimDisappearAnimator(
@@ -230,11 +253,11 @@
             createSuccessOpacityAnimator(),
         )
         return AnimatorSet().apply {
-            playTogether(rimSuccessAnimator, cameraProtectionAnimator)
+            playTogether(rimSuccessAnimator, cameraProtectAnimator)
         }
     }
 
-    private fun createFaceNotSuccessRimAnimator(): AnimatorSet {
+    private fun createFaceNotSuccessRimAnimator(cameraProtectAnimator: ValueAnimator?): AnimatorSet {
         return AnimatorSet().apply {
             playTogether(
                 createRimDisappearAnimator(
@@ -242,7 +265,7 @@
                     PULSE_ERROR_DISAPPEAR_DURATION,
                     Interpolators.STANDARD
                 ),
-                cameraProtectionAnimator,
+                cameraProtectAnimator,
             )
         }
     }
@@ -279,11 +302,11 @@
         }
     }
 
-    private fun createFaceScanningRimAnimator(): AnimatorSet {
+    private fun createFaceScanningRimAnimator(cameraProtectAnimator: ValueAnimator?): AnimatorSet {
         return AnimatorSet().apply {
             playSequentially(
-                    cameraProtectionAnimator,
-                    createRimAppearAnimator(),
+                cameraProtectAnimator,
+                createRimAppearAnimator(),
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 13cd2c5..19b2920 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -19,6 +19,7 @@
 import static androidx.dynamicanimation.animation.DynamicAnimation.TRANSLATION_X;
 import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
 
+import static com.android.systemui.Flags.magneticNotificationSwipes;
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
 import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
 
@@ -76,8 +77,7 @@
 
     protected final Handler mHandler;
 
-    private final SpringConfig mSnapBackSpringConfig =
-            new SpringConfig(SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+    private final SpringConfig mSnapBackSpringConfig;
 
     private final FlingAnimationUtils mFlingAnimationUtils;
     private float mPagingTouchSlop;
@@ -153,6 +153,12 @@
                 R.bool.config_fadeDependingOnAmountSwiped);
         mFalsingManager = falsingManager;
         mFeatureFlags = featureFlags;
+        if (magneticNotificationSwipes()) {
+            mSnapBackSpringConfig = new SpringConfig(550f /*stiffness*/, 0.52f /*dampingRatio*/);
+        } else {
+            mSnapBackSpringConfig = new SpringConfig(
+                    SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+        }
         mFlingAnimationUtils = new FlingAnimationUtils(resources.getDisplayMetrics(),
                 getMaxEscapeAnimDuration() / 1000f);
     }
@@ -718,7 +724,7 @@
                         dismissChild(mTouchedView, velocity,
                                 !swipedFastEnough() /* useAccelerateInterpolator */);
                     } else {
-                        mCallback.onDragCancelled(mTouchedView);
+                        mCallback.onDragCancelledWithVelocity(mTouchedView, velocity);
                         snapChild(mTouchedView, 0 /* leftTarget */, velocity);
                     }
                     mTouchedView = null;
@@ -925,6 +931,15 @@
         void onDragCancelled(View v);
 
         /**
+         * A drag operation has been cancelled on a view with a final velocity.
+         * @param v View that was dragged.
+         * @param finalVelocity Final velocity of the drag.
+         */
+        default void onDragCancelledWithVelocity(View v, float finalVelocity) {
+            onDragCancelled(v);
+        }
+
+        /**
          * Called when the child is long pressed and available to start drag and drop.
          *
          * @param v the view that was long pressed.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index 5cba464..5482c3d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -50,7 +50,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.util.settings.SecureSettings;
@@ -79,7 +79,7 @@
     private final Executor mExecutor;
     private final AccessibilityManager mAccessibilityManager;
     private final CommandQueue mCommandQueue;
-    private final OverviewProxyService mOverviewProxyService;
+    private final LauncherProxyService mLauncherProxyService;
     private final DisplayTracker mDisplayTracker;
     private final AccessibilityLogger mA11yLogger;
 
@@ -225,13 +225,13 @@
     public MagnificationImpl(Context context,
             @Main Handler mainHandler, @Main Executor executor,
             CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
-            SysUiState sysUiState, OverviewProxyService overviewProxyService,
+            SysUiState sysUiState, LauncherProxyService launcherProxyService,
             SecureSettings secureSettings, DisplayTracker displayTracker,
             DisplayManager displayManager, AccessibilityLogger a11yLogger,
             IWindowManager iWindowManager, AccessibilityManager accessibilityManager,
             ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
         this(context, mainHandler.getLooper(), executor, commandQueue,
-                modeSwitchesController, sysUiState, overviewProxyService, secureSettings,
+                modeSwitchesController, sysUiState, launcherProxyService, secureSettings,
                 displayTracker, displayManager, a11yLogger, iWindowManager, accessibilityManager,
                 viewCaptureAwareWindowManager);
     }
@@ -239,7 +239,7 @@
     @VisibleForTesting
     public MagnificationImpl(Context context, Looper looper, @Main Executor executor,
             CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
-            SysUiState sysUiState, OverviewProxyService overviewProxyService,
+            SysUiState sysUiState, LauncherProxyService launcherProxyService,
             SecureSettings secureSettings, DisplayTracker displayTracker,
             DisplayManager displayManager, AccessibilityLogger a11yLogger,
             IWindowManager iWindowManager,
@@ -258,7 +258,7 @@
         mCommandQueue = commandQueue;
         mModeSwitchesController = modeSwitchesController;
         mSysUiState = sysUiState;
-        mOverviewProxyService = overviewProxyService;
+        mLauncherProxyService = launcherProxyService;
         mDisplayTracker = displayTracker;
         mA11yLogger = a11yLogger;
         mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
@@ -279,7 +279,7 @@
     @Override
     public void start() {
         mCommandQueue.addCallback(this);
-        mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
+        mLauncherProxyService.addCallback(new LauncherProxyService.LauncherProxyListener() {
             @Override
             public void onConnectionChanged(boolean isConnected) {
                 if (isConnected) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
index 39fd44a..2669348 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
@@ -27,7 +27,6 @@
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -49,7 +48,6 @@
     suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean)
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
 class CaptioningRepositoryImpl
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 939d96e..da1c1bc 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -37,7 +37,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
@@ -142,7 +142,7 @@
     protected final Context mContext;
     private final AssistDisclosure mAssistDisclosure;
     private final PhoneStateMonitor mPhoneStateMonitor;
-    private final OverviewProxyService mOverviewProxyService;
+    private final LauncherProxyService mLauncherProxyService;
     private final UiController mUiController;
     protected final Lazy<SysUiState> mSysUiState;
     protected final AssistLogger mAssistLogger;
@@ -176,7 +176,7 @@
     private final CommandQueue mCommandQueue;
     protected final AssistUtils mAssistUtils;
 
-    // Invocation types that should be sent over OverviewProxy instead of handled here.
+    // Invocation types that should be sent over LauncherProxy instead of handled here.
     private int[] mAssistOverrideInvocationTypes;
 
     @Inject
@@ -186,7 +186,7 @@
             AssistUtils assistUtils,
             CommandQueue commandQueue,
             PhoneStateMonitor phoneStateMonitor,
-            OverviewProxyService overviewProxyService,
+            LauncherProxyService launcherProxyService,
             Lazy<SysUiState> sysUiState,
             DefaultUiController defaultUiController,
             AssistLogger assistLogger,
@@ -203,7 +203,7 @@
         mCommandQueue = commandQueue;
         mAssistUtils = assistUtils;
         mAssistDisclosure = new AssistDisclosure(context, uiHandler, viewCaptureAwareWindowManager);
-        mOverviewProxyService = overviewProxyService;
+        mLauncherProxyService = launcherProxyService;
         mPhoneStateMonitor = phoneStateMonitor;
         mAssistLogger = assistLogger;
         mUserTracker = userTracker;
@@ -220,7 +220,7 @@
 
         mSysUiState = sysUiState;
 
-        mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
+        mLauncherProxyService.addCallback(new LauncherProxyService.LauncherProxyListener() {
             @Override
             public void onAssistantProgress(float progress) {
                 // Progress goes from 0 to 1 to indicate how close the assist gesture is to
@@ -288,14 +288,14 @@
         }
         if (shouldOverrideAssist(args)) {
             try {
-                if (mOverviewProxyService.getProxy() == null) {
-                    Log.w(TAG, "No OverviewProxyService to invoke assistant override");
+                if (mLauncherProxyService.getProxy() == null) {
+                    Log.w(TAG, "No LauncherProxyService to invoke assistant override");
                     return;
                 }
-                mOverviewProxyService.getProxy().onAssistantOverrideInvoked(
+                mLauncherProxyService.getProxy().onAssistantOverrideInvoked(
                         args.getInt(INVOCATION_TYPE_KEY));
             } catch (RemoteException e) {
-                Log.w(TAG, "Unable to invoke assistant via OverviewProxyService override", e);
+                Log.w(TAG, "Unable to invoke assistant via LauncherProxyService override", e);
             }
             return;
         }
@@ -333,7 +333,7 @@
         return shouldOverrideAssist(invocationType);
     }
 
-    /** @return true if the invocation type should be handled by OverviewProxy instead of SysUI. */
+    /** @return true if the invocation type should be handled by LauncherProxy instead of SysUI. */
     public boolean shouldOverrideAssist(int invocationType) {
         return mAssistOverrideInvocationTypes != null
                 && Arrays.stream(mAssistOverrideInvocationTypes).anyMatch(
@@ -342,7 +342,7 @@
 
     /**
      * @param invocationTypes The invocation types that will henceforth be handled via
-     *                        OverviewProxy (Launcher); other invocation types should be handled by
+     *                        LauncherProxy (Launcher); other invocation types should be handled by
      *                        this class.
      */
     public void setAssistantOverridesRequested(int[] invocationTypes) {
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index a061d38..27250df 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.authentication.data.repository
 
 import android.annotation.UserIdInt
@@ -48,7 +46,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 1c99473..1264712 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -32,6 +32,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
@@ -66,6 +69,7 @@
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val repository: AuthenticationRepository,
     private val selectedUserInteractor: SelectedUserInteractor,
+    @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
 ) {
     /**
      * The currently-configured authentication method. This determines how the authentication
@@ -85,7 +89,11 @@
      * `true` even when the lockscreen is showing and still needs to be dismissed by the user to
      * proceed.
      */
-    val authenticationMethod: Flow<AuthenticationMethodModel> = repository.authenticationMethod
+    val authenticationMethod: Flow<AuthenticationMethodModel> =
+        repository.authenticationMethod.logDiffsForTable(
+            tableLogBuffer = tableLogBuffer,
+            initialValue = AuthenticationMethodModel.None,
+        )
 
     /**
      * Whether the auto confirm feature is enabled for the currently-selected user.
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
index 4e45fcc..744fd7e 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.authentication.shared.model
 
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
 /** Enumerates all known authentication methods. */
 sealed class AuthenticationMethodModel(
     /**
@@ -24,8 +27,8 @@
      * "Secure" authentication methods require authentication to unlock the device. Non-secure auth
      * methods simply require user dismissal.
      */
-    open val isSecure: Boolean,
-) {
+    open val isSecure: Boolean
+) : Diffable<AuthenticationMethodModel> {
     /**
      * Device doesn't use a secure authentication method. Either there is no lockscreen or the lock
      * screen can be swiped away when displayed.
@@ -39,4 +42,8 @@
     data object Pattern : AuthenticationMethodModel(isSecure = true)
 
     data object Sim : AuthenticationMethodModel(isSecure = true)
+
+    override fun logDiffs(prevVal: AuthenticationMethodModel, row: TableRowLogger) {
+        row.logChange(columnName = "authenticationMethod", value = toString())
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index a9133e4..fd11fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -61,7 +61,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.OptIn;
 
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.R;
@@ -113,7 +112,6 @@
 import kotlin.Unit;
 
 import kotlinx.coroutines.CoroutineScope;
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -264,7 +262,6 @@
     }
 
     public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
-        @OptIn(markerClass = ExperimentalCoroutinesApi.class)
         @Override
         public void showUdfpsOverlay(long requestId, int sensorId, int reason,
                 @NonNull IUdfpsOverlayControllerCallback callback) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 671ca8b..702f237 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -69,7 +69,6 @@
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import dagger.Lazy
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
@@ -83,7 +82,6 @@
  * Keeps track of the overlay state and UI resources associated with a single FingerprintService
  * request. This state can persist across configuration changes via the [show] and [hide] methods.
  */
-@ExperimentalCoroutinesApi
 @UiThread
 class UdfpsControllerOverlay
 @JvmOverloads
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt
index afe37d4..abdcbc0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt
@@ -18,10 +18,8 @@
 
 import android.content.Context
 import android.util.AttributeSet
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** View corresponding with udfps_keyguard_view.xml */
-@ExperimentalCoroutinesApi
 class UdfpsKeyguardView(
     context: Context,
     attrs: AttributeSet?,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
index 6c9be81..0755daf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.biometrics.domain.interactor
 
 import android.hardware.biometrics.AuthenticateOptions
@@ -32,7 +30,6 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
index 52e8557..a174424 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
@@ -34,7 +34,6 @@
 import com.android.systemui.res.R
 import java.util.Optional
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -44,7 +43,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class SideFpsSensorInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index 8fe5ded..6cd763a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -49,12 +49,10 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.combine
 import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Binds the side fingerprint sensor indicator view to [SideFpsOverlayViewModel]. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class SideFpsOverlayViewBinder
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
index 8763e5c..68a4faf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
@@ -24,10 +24,8 @@
 import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay
 import com.android.systemui.biometrics.ui.viewmodel.UdfpsTouchOverlayViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import com.android.app.tracing.coroutines.launchTraced as launch
 
-@ExperimentalCoroutinesApi
 object UdfpsTouchOverlayBinder {
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModel.kt
index f8338ae..905b183 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModel.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.statusbar.phone.hideAffordancesRequest
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 
@@ -31,7 +30,6 @@
  * - shade is fully or partially expanded
  * - any SysUI dialogs are obscuring the display
  */
-@ExperimentalCoroutinesApi
 class DefaultUdfpsTouchOverlayViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt
index 07e30ce..47277e1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.statusbar.phone.hideAffordancesRequest
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -33,7 +32,6 @@
  * touches as long as the device entry view is visible (the lockscreen or the alternate bouncer
  * view).
  */
-@ExperimentalCoroutinesApi
 class DeviceEntryUdfpsTouchOverlayViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
index 5557161..19930ef 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
@@ -39,14 +39,12 @@
 import com.android.systemui.res.R
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 
 /** Models UI of the side fingerprint sensor indicator view. */
-@OptIn(ExperimentalCoroutinesApi::class)
 class SideFpsOverlayViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
index 116e76c..832afb1 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
@@ -22,21 +22,25 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.settingslib.bluetooth.onBroadcastMetadataChanged
+import com.android.settingslib.bluetooth.onBroadcastStartedOrStopped
 import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.drop
 import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterNot
 import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.receiveAsFlow
 import kotlinx.coroutines.withContext
 
 /** Holds business logic for the audio sharing state. */
@@ -63,7 +67,6 @@
 }
 
 @SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
 open class AudioSharingInteractorImpl
 @Inject
 constructor(
@@ -73,6 +76,7 @@
     @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) : AudioSharingInteractor {
 
+    private val audioSharingStartedEvents = Channel<Unit>(Channel.BUFFERED)
     private var previewEnabled: Boolean? = null
 
     override val isAudioSharingOn: Flow<Boolean> =
@@ -101,12 +105,18 @@
         withContext(backgroundDispatcher) {
             if (audioSharingAvailable()) {
                 audioSharingRepository.leAudioBroadcastProfile?.let { profile ->
-                    isAudioSharingOn
-                        // Skip the default value, we only care about adding source for newly
-                        // started audio sharing session
-                        .drop(1)
-                        .mapNotNull { audioSharingOn ->
-                            if (audioSharingOn) {
+                    merge(
+                            // Register and start listen to onBroadcastMetadataChanged (means ready
+                            // to add source)
+                            audioSharingStartedEvents.receiveAsFlow().map { true },
+                            // When session is off or failed to start, stop listening to
+                            // onBroadcastMetadataChanged as we won't be adding source
+                            profile.onBroadcastStartedOrStopped
+                                .filterNot { profile.isEnabled(null) }
+                                .map { false },
+                        )
+                        .mapNotNull { shouldListenToMetadata ->
+                            if (shouldListenToMetadata) {
                                 // onBroadcastMetadataChanged could emit multiple times during one
                                 // audio sharing session, we only perform add source on the first
                                 // time
@@ -148,6 +158,7 @@
         if (!audioSharingAvailable()) {
             return
         }
+        audioSharingStartedEvents.trySend(Unit)
         audioSharingRepository.startAudioSharing()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractor.kt
index d69e416..9019051 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractor.kt
@@ -25,7 +25,6 @@
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChangedBy
@@ -33,7 +32,6 @@
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.merge
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class BluetoothDeviceMetadataInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
index 9460e7c..bf04897 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
@@ -87,7 +87,6 @@
      *
      * @param view The view from which the dialog is shown.
      */
-    @kotlinx.coroutines.ExperimentalCoroutinesApi
     fun showDialog(expandable: Expandable?) {
         cancelJob()
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index e52ddb2..b5a38d8 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -43,7 +43,6 @@
 import javax.inject.Inject
 import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
@@ -58,7 +57,6 @@
 private const val TAG = "BouncerMessageInteractor"
 
 /** Handles business logic for the primary bouncer message area. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class BouncerMessageInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt
index fa57ab9..56a95dd 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt
@@ -26,12 +26,10 @@
 import com.android.systemui.log.BouncerLogger
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.collectLatest
 import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Startable that logs the flows that bouncer depends on. */
-@OptIn(ExperimentalCoroutinesApi::class)
 class BouncerLoggerStartable
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index 8a4cc63..116c941 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -22,10 +22,8 @@
 import java.util.Optional
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Helper data class that allows to lazy load all the dependencies of the legacy bouncer. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 data class LegacyBouncerDependencies
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
index a6874b0..fafde96 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
@@ -48,7 +48,6 @@
 import kotlin.math.ceil
 import kotlin.math.max
 import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.coroutineScope
@@ -64,7 +63,6 @@
 import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Holds UI state for the 2-line status message shown on the bouncer. */
-@OptIn(ExperimentalCoroutinesApi::class)
 class BouncerMessageViewModel
 @AssistedInject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
index 3fef766..24e85fb 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -22,12 +22,10 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.bouncer.ui.BouncerViewDelegate
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
 
 /** Models UI state for the lock screen bouncer; handles user input. */
-@ExperimentalCoroutinesApi
 class KeyguardBouncerViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 73aaf7f..c9ae20d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.bouncer.ui.viewmodel
 
 import android.content.Context
@@ -39,7 +37,6 @@
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.coroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt
index 0544a4f..6e1cd2d 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.utils.UserRestrictionChecker
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.mapLatest
@@ -48,7 +47,6 @@
     }
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class BrightnessPolicyRepositoryImpl
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
index 1923880..79e66a8 100644
--- a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.common.data
 
+import com.android.systemui.common.data.repository.BatteryRepository
+import com.android.systemui.common.data.repository.BatteryRepositoryImpl
 import com.android.systemui.common.data.repository.PackageChangeRepository
 import com.android.systemui.common.data.repository.PackageChangeRepositoryImpl
 import dagger.Binds
@@ -27,4 +29,6 @@
     abstract fun bindPackageChangeRepository(
         impl: PackageChangeRepositoryImpl
     ): PackageChangeRepository
+
+    @Binds abstract fun bindBatteryRepository(impl: BatteryRepositoryImpl): BatteryRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/BatteryRepository.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/BatteryRepository.kt
new file mode 100644
index 0000000..63b0513
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/BatteryRepository.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 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.common.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.util.kotlin.isDevicePluggedIn
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.stateIn
+
+interface BatteryRepository {
+    val isDevicePluggedIn: Flow<Boolean>
+}
+
+@SysUISingleton
+class BatteryRepositoryImpl
+@Inject
+constructor(@Background bgScope: CoroutineScope, batteryController: BatteryController) :
+    BatteryRepository {
+
+    /** Returns {@code true} if the device is currently plugged in or wireless charging. */
+    override val isDevicePluggedIn: Flow<Boolean> =
+        batteryController
+            .isDevicePluggedIn()
+            .stateIn(bgScope, SharingStarted.WhileSubscribed(), batteryController.isPluggedIn)
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/BatteryInteractor.kt
similarity index 62%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
copy to packages/SystemUI/src/com/android/systemui/common/domain/interactor/BatteryInteractor.kt
index aa262f9..987776d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/BatteryInteractor.kt
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.utils
+package com.android.systemui.common.domain.interactor
 
-import android.app.Instrumentation
+import com.android.systemui.common.data.repository.BatteryRepository
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
 
-object RecentTasksUtils {
-    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
-        instrumentation.uiAutomation.executeShellCommand(
-            "dumpsys activity service SystemUIService WMShell recents clearAll"
-        )
-    }
-}
\ No newline at end of file
+@SysUISingleton
+class BatteryInteractor @Inject constructor(batteryRepository: BatteryRepository) {
+    val isDevicePluggedIn = batteryRepository.isDevicePluggedIn
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/PackageChangeInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/PackageChangeInteractor.kt
index 59c1923..0d6f568 100644
--- a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/PackageChangeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/PackageChangeInteractor.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
@@ -31,7 +30,6 @@
  * Allows listening to package updates. This is recommended over registering broadcasts directly as
  * it avoids the delay imposed by broadcasts, and provides more structured updates.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class PackageChangeInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
index 5671fa4..4452627 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
@@ -13,8 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License
  */
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.common.ui.data.repository
 
 import android.content.Context
@@ -36,7 +34,6 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
index 4d39b033..8c123d3 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
@@ -13,15 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License
  */
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.common.ui.domain.interactor
 
 import android.content.res.Configuration
 import android.graphics.Rect
 import android.view.Surface
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 34679b0..fc589b2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -54,7 +54,6 @@
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.collectLatest
@@ -69,7 +68,6 @@
  * A [CoreStartable] responsible for automatically navigating between communal scenes when certain
  * conditions are met.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalSceneStartable
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java
new file mode 100644
index 0000000..2e1b5ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java
@@ -0,0 +1,101 @@
+/*
+ * 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.communal;
+
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import javax.inject.Inject;
+
+/**
+ * Condition which estimates device inactivity in order to avoid launching a full-screen activity
+ * while the user is actively using the device.
+ */
+public class DeviceInactiveCondition extends Condition {
+    private final KeyguardStateController mKeyguardStateController;
+    private final WakefulnessLifecycle mWakefulnessLifecycle;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final KeyguardStateController.Callback mKeyguardStateCallback =
+            new KeyguardStateController.Callback() {
+                @Override
+                public void onKeyguardShowingChanged() {
+                    updateState();
+                }
+            };
+    private final WakefulnessLifecycle.Observer mWakefulnessObserver =
+            new WakefulnessLifecycle.Observer() {
+                @Override
+                public void onStartedGoingToSleep() {
+                    updateState();
+                }
+            };
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onDreamingStateChanged(boolean dreaming) {
+                    updateState();
+                }
+            };
+
+    @Inject
+    public DeviceInactiveCondition(@Application CoroutineScope scope,
+            KeyguardStateController keyguardStateController,
+            WakefulnessLifecycle wakefulnessLifecycle,
+            KeyguardUpdateMonitor keyguardUpdateMonitor) {
+        super(scope);
+        mKeyguardStateController = keyguardStateController;
+        mWakefulnessLifecycle = wakefulnessLifecycle;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+    }
+
+    @Override
+    protected void start() {
+        updateState();
+        mKeyguardStateController.addCallback(mKeyguardStateCallback);
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+        mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+    }
+
+    @Override
+    protected void stop() {
+        mKeyguardStateController.removeCallback(mKeyguardStateCallback);
+        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+        mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
+    }
+
+    @Override
+    protected int getStartStrategy() {
+        return START_EAGERLY;
+    }
+
+    private void updateState() {
+        final boolean asleep =
+                mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
+                        || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP;
+        updateCondition(asleep || mKeyguardStateController.isShowing()
+                || mKeyguardUpdateMonitor.isDreaming());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
index 47040fa..af8a5fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
@@ -58,7 +58,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 tableLogBuffer = tableLogBuffer,
-                columnPrefix = "",
                 columnName = "postured",
                 initialValue = false,
             )
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 882991aa..b89d322 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
@@ -52,7 +52,6 @@
     override val mediaModel: Flow<CommunalMediaModel> =
         _mediaModel.logDiffsForTable(
             tableLogBuffer = tableLogBuffer,
-            columnPrefix = "",
             initialValue = CommunalMediaModel.INACTIVE,
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
index a02bc8b..b747690 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.util.kotlin.emitOnStart
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
@@ -57,9 +56,14 @@
 
     /** Save the hub onboarding dismissed state for the current user. */
     suspend fun setHubOnboardingDismissed(user: UserInfo)
+
+    /** Whether dream button tooltip has been dismissed. */
+    fun isDreamButtonTooltipDismissed(user: UserInfo): Flow<Boolean>
+
+    /** Save the dream button tooltip dismissed state for the current user. */
+    suspend fun setDreamButtonTooltipDismissed(user: UserInfo)
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalPrefsRepositoryImpl
 @Inject
@@ -89,27 +93,34 @@
         readKeyForUser(user, CTA_DISMISSED_STATE)
 
     override suspend fun setCtaDismissed(user: UserInfo) =
-        withContext(bgDispatcher) {
-            getSharedPrefsForUser(user).edit().putBoolean(CTA_DISMISSED_STATE, true).apply()
-            logger.i("Dismissed CTA tile")
-        }
+        setBooleanKeyValueForUser(user, CTA_DISMISSED_STATE, "Dismissed CTA tile")
 
     override fun isHubOnboardingDismissed(user: UserInfo): Flow<Boolean> =
         readKeyForUser(user, HUB_ONBOARDING_DISMISSED_STATE)
 
     override suspend fun setHubOnboardingDismissed(user: UserInfo) =
-        withContext(bgDispatcher) {
-            getSharedPrefsForUser(user)
-                .edit()
-                .putBoolean(HUB_ONBOARDING_DISMISSED_STATE, true)
-                .apply()
-            logger.i("Dismissed hub onboarding")
-        }
+        setBooleanKeyValueForUser(user, HUB_ONBOARDING_DISMISSED_STATE, "Dismissed hub onboarding")
+
+    override fun isDreamButtonTooltipDismissed(user: UserInfo): Flow<Boolean> =
+        readKeyForUser(user, DREAM_BUTTON_TOOLTIP_DISMISSED_STATE)
+
+    override suspend fun setDreamButtonTooltipDismissed(user: UserInfo) =
+        setBooleanKeyValueForUser(
+            user,
+            DREAM_BUTTON_TOOLTIP_DISMISSED_STATE,
+            "Dismissed dream button tooltip",
+        )
 
     private fun getSharedPrefsForUser(user: UserInfo): SharedPreferences {
         return userFileManager.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, user.id)
     }
 
+    private suspend fun setBooleanKeyValueForUser(user: UserInfo, key: String, logMsg: String) =
+        withContext(bgDispatcher) {
+            getSharedPrefsForUser(user).edit().putBoolean(key, true).apply()
+            logger.i(logMsg)
+        }
+
     private fun readKeyForUser(user: UserInfo, key: String): Flow<Boolean> {
         return backupRestorationEvents
             .flatMapLatest {
@@ -124,5 +135,6 @@
         const val FILE_NAME = "communal_hub_prefs"
         const val CTA_DISMISSED_STATE = "cta_dismissed"
         const val HUB_ONBOARDING_DISMISSED_STATE = "hub_onboarding_dismissed"
+        const val DREAM_BUTTON_TOOLTIP_DISMISSED_STATE = "dream_button_tooltip_dismissed_state"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
index 415fd4e..643d185 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.scene.shared.model.SceneDataSource
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -61,7 +60,6 @@
     fun setTransitionState(transitionState: Flow<ObservableTransitionState>?)
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalSceneRepositoryImpl
 @Inject
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 73c0179..abd1016 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
@@ -32,7 +32,9 @@
 import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
 import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_INVALID_USER
 import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule.Companion.DEFAULT_BACKGROUND_TYPE
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
+import com.android.systemui.communal.shared.model.WhenToDream
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -43,6 +45,7 @@
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import java.util.EnumSet
 import javax.inject.Inject
+import javax.inject.Named
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -58,6 +61,12 @@
     fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean>
 
     /**
+     * Returns a [WhenToDream] for the specified user, indicating what state the device should be in
+     * to trigger dreams.
+     */
+    fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream>
+
+    /**
      * Returns true if any glanceable hub functionality should be enabled via configs and flags.
      *
      * This should be used for preventing basic glanceable hub functionality from running on devices
@@ -99,6 +108,7 @@
     private val secureSettings: SecureSettings,
     private val broadcastDispatcher: BroadcastDispatcher,
     private val devicePolicyManager: DevicePolicyManager,
+    @Named(DEFAULT_BACKGROUND_TYPE) private val defaultBackgroundType: CommunalBackgroundType,
 ) : CommunalSettingsRepository {
 
     override fun getFlagEnabled(): Boolean {
@@ -154,6 +164,49 @@
             }
             .flowOn(bgDispatcher)
 
+    override fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream> =
+        secureSettings
+            .observerFlow(
+                userId = user.id,
+                names =
+                    arrayOf(
+                        Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+                        Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+                        Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+                    ),
+            )
+            .emitOnStart()
+            .map {
+                if (
+                    secureSettings.getIntForUser(
+                        Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+                        0,
+                        user.id,
+                    ) == 1
+                ) {
+                    WhenToDream.WHILE_CHARGING
+                } else if (
+                    secureSettings.getIntForUser(
+                        Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+                        0,
+                        user.id,
+                    ) == 1
+                ) {
+                    WhenToDream.WHILE_DOCKED
+                } else if (
+                    secureSettings.getIntForUser(
+                        Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+                        0,
+                        user.id,
+                    ) == 1
+                ) {
+                    WhenToDream.WHILE_POSTURED
+                } else {
+                    WhenToDream.NEVER
+                }
+            }
+            .flowOn(bgDispatcher)
+
     override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
         broadcastDispatcher
             .broadcastFlow(
@@ -175,11 +228,11 @@
                 val intType =
                     secureSettings.getIntForUser(
                         GLANCEABLE_HUB_BACKGROUND_SETTING,
-                        CommunalBackgroundType.ANIMATED.value,
+                        defaultBackgroundType.value,
                         user.id,
                     )
                 CommunalBackgroundType.entries.find { type -> type.value == intType }
-                    ?: CommunalBackgroundType.ANIMATED
+                    ?: defaultBackgroundType
             }
 
     private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
index a931d3f..0f25225 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
@@ -16,11 +16,29 @@
 
 package com.android.systemui.communal.data.repository
 
+import com.android.systemui.Flags.glanceableHubBlurredBackground
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import dagger.Binds
 import dagger.Module
+import dagger.Provides
+import javax.inject.Named
 
 @Module
 interface CommunalSettingsRepositoryModule {
+    companion object {
+        const val DEFAULT_BACKGROUND_TYPE = "default_background_type"
+
+        @Provides
+        @Named(DEFAULT_BACKGROUND_TYPE)
+        fun providesDefaultBackgroundType(): CommunalBackgroundType {
+            if (glanceableHubBlurredBackground()) {
+                return CommunalBackgroundType.BLUR
+            }
+
+            return CommunalBackgroundType.ANIMATED
+        }
+    }
+
     @Binds
     fun communalSettingsRepository(impl: CommunalSettingsRepositoryImpl): CommunalSettingsRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
index 4c06e97..1b0a6a0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
@@ -34,7 +34,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -59,7 +58,6 @@
     suspend fun setTutorialState(@HubModeTutorialState state: Int)
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalTutorialRepositoryImpl
 @Inject
@@ -100,7 +98,6 @@
             .filterNotNull()
             .logDiffsForTable(
                 tableLogBuffer = tableLogBuffer,
-                columnPrefix = "",
                 columnName = "tutorialSettingState",
                 initialValue = HUB_MODE_TUTORIAL_NOT_STARTED,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index e44d78b..873399f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -47,7 +47,6 @@
 import kotlin.coroutines.cancellation.CancellationException
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
@@ -168,7 +167,6 @@
         }
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
         widgetEntries
             .flatMapLatest { widgetEntries ->
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 947113d..237a19c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -28,12 +28,16 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.Flags.communalResponsiveGrid
+import com.android.systemui.Flags.glanceableHubBlurredBackground
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.domain.interactor.BatteryInteractor
 import com.android.systemui.communal.data.repository.CommunalMediaRepository
 import com.android.systemui.communal.data.repository.CommunalSmartspaceRepository
 import com.android.systemui.communal.data.repository.CommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.domain.model.CommunalContentModel.WidgetContent
+import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.FULL
 import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.HALF
@@ -41,11 +45,14 @@
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.shared.model.EditModeState
+import com.android.systemui.communal.shared.model.WhenToDream
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.retrieveIsDocked
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -65,11 +72,11 @@
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.kotlin.isDevicePluggedIn
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.minutes
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
@@ -85,6 +92,7 @@
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
@@ -92,7 +100,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates business-logic related to communal mode. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalInteractor
 @Inject
@@ -117,6 +124,9 @@
     @CommunalLog logBuffer: LogBuffer,
     @CommunalTableLog tableLogBuffer: TableLogBuffer,
     private val managedProfileController: ManagedProfileController,
+    private val batteryInteractor: BatteryInteractor,
+    private val dockManager: DockManager,
+    private val posturingInteractor: PosturingInteractor,
 ) {
     private val logger = Logger(logBuffer, "CommunalInteractor")
 
@@ -163,7 +173,6 @@
             }
             .logDiffsForTable(
                 tableLogBuffer = tableLogBuffer,
-                columnPrefix = "",
                 columnName = "isCommunalAvailable",
                 initialValue = false,
             )
@@ -173,6 +182,37 @@
                 replay = 1,
             )
 
+    /**
+     * Whether communal hub should be shown automatically, depending on the user's [WhenToDream]
+     * state.
+     */
+    val shouldShowCommunal: StateFlow<Boolean> =
+        allOf(
+                isCommunalAvailable,
+                communalSettingsInteractor.whenToDream
+                    .flatMapLatest { whenToDream ->
+                        when (whenToDream) {
+                            WhenToDream.NEVER -> flowOf(false)
+
+                            WhenToDream.WHILE_CHARGING -> batteryInteractor.isDevicePluggedIn
+
+                            WhenToDream.WHILE_DOCKED ->
+                                allOf(
+                                    batteryInteractor.isDevicePluggedIn,
+                                    dockManager.retrieveIsDocked(),
+                                )
+
+                            WhenToDream.WHILE_POSTURED ->
+                                allOf(
+                                    batteryInteractor.isDevicePluggedIn,
+                                    posturingInteractor.postured,
+                                )
+                        }
+                    }
+                    .flowOn(bgDispatcher),
+            )
+            .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+
     private val _isDisclaimerDismissed = MutableStateFlow(false)
     val isDisclaimerDismissed: Flow<Boolean> = _isDisclaimerDismissed.asStateFlow()
 
@@ -300,7 +340,6 @@
             }
             .logDiffsForTable(
                 tableLogBuffer = tableLogBuffer,
-                columnPrefix = "",
                 columnName = "isCommunalShowing",
                 initialValue = false,
             )
@@ -311,6 +350,25 @@
             )
 
     /**
+     * Flow that emits {@code true} whenever communal is influencing the shown background on the
+     * screen. This happens when the background for communal is set to blur and communal is visible.
+     * This is used by other components to determine when blur-related emitted values for communal
+     * should be considered.
+     */
+    val isCommunalBlurring: StateFlow<Boolean> =
+        communalSceneInteractor.isCommunalVisible
+            .combine(communalSettingsInteractor.communalBackground) { showing, background ->
+                showing &&
+                    background == CommunalBackgroundType.BLUR &&
+                    glanceableHubBlurredBackground()
+            }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = false,
+            )
+
+    /**
      * Flow that emits a boolean if the communal UI is fully visible and not in transition.
      *
      * This will not be true while transitioning to the hub and will turn false immediately when a
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
index 76e6cde4..cdf1703 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
@@ -27,14 +27,12 @@
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalPrefsInteractor
 @Inject
@@ -51,7 +49,6 @@
             .flatMapLatest { user -> repository.isCtaDismissed(user) }
             .logDiffsForTable(
                 tableLogBuffer = tableLogBuffer,
-                columnPrefix = "",
                 columnName = "isCtaDismissed",
                 initialValue = false,
             )
@@ -69,7 +66,6 @@
             .flatMapLatest { user -> repository.isHubOnboardingDismissed(user) }
             .logDiffsForTable(
                 tableLogBuffer = tableLogBuffer,
-                columnPrefix = "",
                 columnName = "isHubOnboardingDismissed",
                 initialValue = false,
             )
@@ -82,6 +78,24 @@
     fun setHubOnboardingDismissed(user: UserInfo = userTracker.userInfo) =
         bgScope.launch { repository.setHubOnboardingDismissed(user) }
 
+    val isDreamButtonTooltipDismissed: Flow<Boolean> =
+        userInteractor.selectedUserInfo
+            .flatMapLatest { user -> repository.isDreamButtonTooltipDismissed(user) }
+            .logDiffsForTable(
+                tableLogBuffer = tableLogBuffer,
+                columnPrefix = "",
+                columnName = "isDreamButtonTooltipDismissed",
+                initialValue = false,
+            )
+            .stateIn(
+                scope = bgScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false,
+            )
+
+    fun setDreamButtonTooltipDismissed(user: UserInfo = userTracker.userInfo) =
+        bgScope.launch { repository.setDreamButtonTooltipDismissed(user) }
+
     private companion object {
         const val TAG = "CommunalPrefsInteractor"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index ae08f2b..4764932 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.util.kotlin.pairwiseBy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -51,7 +50,6 @@
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalSceneInteractor
 @Inject
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 c1f21e40..a0b1261 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
@@ -21,6 +21,7 @@
 import com.android.systemui.communal.data.model.CommunalEnabledState
 import com.android.systemui.communal.data.repository.CommunalSettingsRepository
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
+import com.android.systemui.communal.shared.model.WhenToDream
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.log.dagger.CommunalTableLog
@@ -32,7 +33,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -43,7 +43,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalSettingsInteractor
 @Inject
@@ -75,6 +74,12 @@
             repository.getScreensaverEnabledState(user)
         }
 
+    /** When to dream for the currently selected user. */
+    val whenToDream: Flow<WhenToDream> =
+        userInteractor.selectedUserInfo.flatMapLatest { user ->
+            repository.getWhenToDreamState(user)
+        }
+
     /**
      * Returns true if any glanceable hub functionality should be enabled via configs and flags.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 6fa0663..8f55d96 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.log.table.logDiffsForTable
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -42,7 +41,6 @@
 import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Encapsulates business-logic related to communal tutorial state. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalTutorialInteractor
 @Inject
@@ -67,7 +65,6 @@
             }
             .logDiffsForTable(
                 tableLogBuffer = tableLogBuffer,
-                columnPrefix = "",
                 columnName = "isTutorialAvailable",
                 initialValue = false,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt
index f49394a..02d0266 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt
@@ -38,7 +38,6 @@
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.takeWhile
@@ -51,7 +50,6 @@
  * widget interaction occurring. Used for detecting non-activity trampolines which otherwise would
  * not prompt the user for authentication.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class WidgetTrampolineInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
index 3b23ba9..e1128ed 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
@@ -22,4 +22,5 @@
     STATIC_GRADIENT(1),
     ANIMATED(2),
     NONE(3),
+    BLUR(4),
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToDream.kt
similarity index 64%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
copy to packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToDream.kt
index aa262f9..0d4eb60c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToDream.kt
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.utils
+package com.android.systemui.communal.shared.model
 
-import android.app.Instrumentation
-
-object RecentTasksUtils {
-    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
-        instrumentation.uiAutomation.executeShellCommand(
-            "dumpsys activity service SystemUIService WMShell recents clearAll"
-        )
-    }
-}
\ No newline at end of file
+enum class WhenToDream {
+    NEVER,
+    WHILE_CHARGING,
+    WHILE_DOCKED,
+    WHILE_POSTURED,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalLockIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalLockIconViewBinder.kt
new file mode 100644
index 0000000..b1407da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalLockIconViewBinder.kt
@@ -0,0 +1,162 @@
+/*
+ * 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.communal.ui.binder
+
+import android.annotation.SuppressLint
+import android.content.res.ColorStateList
+import android.util.Log
+import android.util.StateSet
+import android.view.HapticFeedbackConstants
+import android.view.View
+import androidx.core.view.isInvisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.communal.ui.viewmodel.CommunalLockIconViewModel
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.kotlin.DisposableHandles
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DisposableHandle
+
+object CommunalLockIconViewBinder {
+    private const val TAG = "CommunalLockIconViewBinder"
+
+    /**
+     * Updates UI for:
+     * - device entry containing view (parent view for the below views)
+     *     - long-press handling view (transparent, no UI)
+     *     - foreground icon view (lock/unlock)
+     */
+    @SuppressLint("ClickableViewAccessibility")
+    @JvmStatic
+    fun bind(
+        applicationScope: CoroutineScope,
+        view: DeviceEntryIconView,
+        viewModel: CommunalLockIconViewModel,
+        falsingManager: FalsingManager,
+        vibratorHelper: VibratorHelper,
+    ): DisposableHandle {
+        val disposables = DisposableHandles()
+        val longPressHandlingView = view.longPressHandlingView
+        val fgIconView = view.iconView
+        val bgView = view.bgView
+        longPressHandlingView.listener =
+            object : LongPressHandlingView.Listener {
+                override fun onLongPressDetected(
+                    view: View,
+                    x: Int,
+                    y: Int,
+                    isA11yAction: Boolean,
+                ) {
+                    if (
+                        !isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
+                    ) {
+                        Log.d(
+                            TAG,
+                            "Long press rejected because it is not a11yAction " +
+                                "and it is a falseLongTap",
+                        )
+                        return
+                    }
+                    vibratorHelper.performHapticFeedback(view, HapticFeedbackConstants.CONFIRM)
+                    applicationScope.launch {
+                        view.clearFocus()
+                        view.clearAccessibilityFocus()
+                        viewModel.onUserInteraction()
+                    }
+                }
+            }
+
+        longPressHandlingView.isInvisible = false
+        view.isClickable = true
+        longPressHandlingView.longPressDuration = {
+            view.resources.getInteger(R.integer.config_lockIconLongPress).toLong()
+        }
+        bgView.visibility = View.GONE
+
+        disposables +=
+            view.repeatWhenAttached {
+                repeatOnLifecycle(Lifecycle.State.CREATED) {
+                    launch("$TAG#viewModel.isLongPressEnabled") {
+                        viewModel.isLongPressEnabled.collect { isEnabled ->
+                            longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
+                        }
+                    }
+                    launch("$TAG#viewModel.accessibilityDelegateHint") {
+                        viewModel.accessibilityDelegateHint.collect { hint ->
+                            view.accessibilityHintType = hint
+                            if (hint != DeviceEntryIconView.AccessibilityHintType.NONE) {
+                                view.setOnClickListener {
+                                    vibratorHelper.performHapticFeedback(
+                                        view,
+                                        HapticFeedbackConstants.CONFIRM,
+                                    )
+                                    applicationScope.launch {
+                                        view.clearFocus()
+                                        view.clearAccessibilityFocus()
+                                        viewModel.onUserInteraction()
+                                    }
+                                }
+                            } else {
+                                view.setOnClickListener(null)
+                            }
+                        }
+                    }
+                }
+            }
+
+        disposables +=
+            fgIconView.repeatWhenAttached {
+                repeatOnLifecycle(Lifecycle.State.STARTED) {
+                    // Start with an empty state
+                    fgIconView.setImageState(StateSet.NOTHING, /* merge */ false)
+                    launch("$TAG#fpIconView.viewModel") {
+                        viewModel.viewAttributes.collect { attributes ->
+                            if (attributes.type.contentDescriptionResId != -1) {
+                                fgIconView.contentDescription =
+                                    fgIconView.resources.getString(
+                                        attributes.type.contentDescriptionResId
+                                    )
+                            }
+                            fgIconView.imageTintList = ColorStateList.valueOf(attributes.tint)
+                            fgIconView.setPadding(
+                                attributes.padding,
+                                attributes.padding,
+                                attributes.padding,
+                                attributes.padding,
+                            )
+                            // Set image state at the end after updating other view state. This
+                            // method forces the ImageView to recompute the bounds of the drawable.
+                            fgIconView.setImageState(
+                                view.getIconState(attributes.type, false),
+                                /* merge */ false,
+                            )
+                            // Invalidate, just in case the padding changes just after icon changes
+                            fgIconView.invalidate()
+                        }
+                    }
+                }
+            }
+        return disposables
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt
new file mode 100644
index 0000000..19eeabd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2025 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.communal.ui.viewmodel
+
+import android.content.Context
+import com.android.keyguard.KeyguardViewController
+import com.android.settingslib.Utils
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntrySourceInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.keyguard.ui.viewmodel.toAccessibilityHintType
+import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.util.kotlin.emitOnStart
+import dagger.Lazy
+import javax.inject.Inject
+import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/**
+ * Simpler implementation of [DeviceEntryIconViewModel] for use in glanceable hub, where fingerprint
+ * is not supported.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class CommunalLockIconViewModel
+@Inject
+constructor(
+    @ShadeDisplayAware val context: Context,
+    @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
+    private val deviceEntryInteractor: DeviceEntryInteractor,
+    keyguardInteractor: KeyguardInteractor,
+    private val keyguardViewController: Lazy<KeyguardViewController>,
+    private val deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
+    accessibilityInteractor: AccessibilityInteractor,
+) {
+
+    private val isUnlocked: Flow<Boolean> =
+        if (SceneContainerFlag.isEnabled) {
+                deviceEntryInteractor.isUnlocked
+            } else {
+                keyguardInteractor.isKeyguardDismissible
+            }
+            .flatMapLatest { isUnlocked ->
+                if (!isUnlocked) {
+                    flowOf(false)
+                } else {
+                    flow {
+                        // delay in case device ends up transitioning away from the lock screen;
+                        // we don't want to animate to the unlocked icon and just let the
+                        // icon fade with the transition to GONE
+                        delay(DeviceEntryIconViewModel.UNLOCKED_DELAY_MS)
+                        emit(true)
+                    }
+                }
+            }
+
+    private val iconType: Flow<DeviceEntryIconView.IconType> =
+        isUnlocked.map { unlocked ->
+            if (unlocked) {
+                DeviceEntryIconView.IconType.UNLOCK
+            } else {
+                DeviceEntryIconView.IconType.LOCK
+            }
+        }
+
+    val isLongPressEnabled: Flow<Boolean> =
+        iconType.map { deviceEntryStatus ->
+            when (deviceEntryStatus) {
+                DeviceEntryIconView.IconType.UNLOCK -> true
+                DeviceEntryIconView.IconType.LOCK,
+                DeviceEntryIconView.IconType.FINGERPRINT,
+                DeviceEntryIconView.IconType.NONE -> false
+            }
+        }
+
+    val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
+        accessibilityInteractor.isEnabled.flatMapLatest { touchExplorationEnabled ->
+            if (touchExplorationEnabled) {
+                iconType.map { it.toAccessibilityHintType() }
+            } else {
+                flowOf(DeviceEntryIconView.AccessibilityHintType.NONE)
+            }
+        }
+
+    private val padding: Flow<Int> =
+        configurationInteractor.scaleForResolution.map { scale ->
+            (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+                .roundToInt()
+        }
+
+    private fun getColor() =
+        Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
+
+    private val color: Flow<Int> =
+        configurationInteractor.onAnyConfigurationChange
+            .emitOnStart()
+            .map { getColor() }
+            .distinctUntilChanged()
+
+    suspend fun onUserInteraction() {
+        if (SceneContainerFlag.isEnabled) {
+            deviceEntryInteractor.attemptDeviceEntry()
+        } else {
+            keyguardViewController.get().showPrimaryBouncer(/* scrim */ true)
+        }
+        deviceEntrySourceInteractor.attemptEnterDeviceFromDeviceEntryIcon()
+    }
+
+    val viewAttributes: Flow<CommunalLockIconAttributes> =
+        combine(iconType, color, padding) { iconType, color, padding ->
+            CommunalLockIconAttributes(type = iconType, tint = color, padding = padding)
+        }
+}
+
+data class CommunalLockIconAttributes(
+    val type: DeviceEntryIconView.IconType,
+    val tint: Int,
+    val padding: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
index bbb1686..7e683c4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
@@ -20,11 +20,14 @@
 import android.app.DreamManager
 import android.content.Intent
 import android.provider.Settings
+import androidx.compose.runtime.getValue
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.communal.domain.interactor.CommunalPrefsInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.util.kotlin.isDevicePluggedIn
@@ -37,7 +40,7 @@
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.receiveAsFlow
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
@@ -47,18 +50,36 @@
 constructor(
     @Background private val backgroundContext: CoroutineContext,
     batteryController: BatteryController,
+    private val prefsInteractor: CommunalPrefsInteractor,
     private val settingsInteractor: CommunalSettingsInteractor,
     private val activityStarter: ActivityStarter,
     private val dreamManager: DreamManager,
     private val uiEventLogger: UiEventLogger,
 ) : ExclusiveActivatable() {
 
+    private val hydrator = Hydrator("CommunalToDreamButtonViewModel.hydrator")
     private val _requests = Channel<Unit>(Channel.BUFFERED)
 
     /** Whether we should show a button on hub to switch to dream. */
-    @SuppressLint("MissingPermission")
-    val shouldShowDreamButtonOnHub =
-        batteryController.isDevicePluggedIn().distinctUntilChanged().flowOn(backgroundContext)
+    val shouldShowDreamButtonOnHub: Boolean by
+        hydrator.hydratedStateOf(
+            traceName = "shouldShowDreamButtonOnHub",
+            initialValue = false,
+            source = batteryController.isDevicePluggedIn().distinctUntilChanged(),
+        )
+
+    /** Return whether the dream button tooltip has been dismissed. */
+    val shouldShowTooltip: Boolean by
+        hydrator.hydratedStateOf(
+            traceName = "shouldShowTooltip",
+            initialValue = false,
+            source = prefsInteractor.isDreamButtonTooltipDismissed.map { !it },
+        )
+
+    /** Set the dream button tooltip to be dismissed. */
+    fun setDreamButtonTooltipDismissed() {
+        prefsInteractor.setDreamButtonTooltipDismissed()
+    }
 
     /** Handle a tap on the "show dream" button. */
     fun onShowDreamButtonTap() {
@@ -86,6 +107,8 @@
                 }
         }
 
+        launch { hydrator.activate() }
+
         awaitCancellation()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
index 3496230..0cbbfd4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
@@ -37,7 +37,6 @@
 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
@@ -49,7 +48,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** View model for transitions related to the communal hub. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 16cf263..4bc4400 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -53,7 +53,6 @@
 import javax.inject.Named
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.delay
@@ -73,7 +72,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** The default view model used for showing the communal hub. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalViewModel
 @Inject
@@ -136,7 +134,6 @@
             }
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     private val latestCommunalContent: Flow<List<CommunalContentModel>> =
         tutorialInteractor.isTutorialAvailable
             .flatMapLatest { isTutorialMode ->
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt
index 691ec76..f7a2c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt
@@ -33,7 +33,6 @@
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
-@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class SelectedComponentRepositoryImpl
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt
index 436b8cb..6f579a3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt
@@ -28,7 +28,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -57,7 +56,6 @@
     override val allowActionOnTrivialControlsInLockscreen =
         makeFlowForSetting(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS)
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     private fun makeFlowForSetting(setting: String): StateFlow<Boolean> {
         return userRepository.selectedUserInfo
             .distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index c02784d..fe5a82c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -81,6 +81,7 @@
 import com.android.systemui.log.dagger.LogModule;
 import com.android.systemui.log.dagger.MonitorLog;
 import com.android.systemui.log.table.TableLogBuffer;
+import com.android.systemui.lowlightclock.dagger.LowLightModule;
 import com.android.systemui.mediaprojection.MediaProjectionModule;
 import com.android.systemui.mediaprojection.appselector.MediaProjectionActivitiesModule;
 import com.android.systemui.mediaprojection.taskswitcher.MediaProjectionTaskSwitcherModule;
@@ -285,7 +286,8 @@
         UserModule.class,
         UtilModule.class,
         NoteTaskModule.class,
-        WalletModule.class
+        WalletModule.class,
+        LowLightModule.class
 },
         subcomponents = {
                 ComplicationComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt b/packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt
index 4d786fe..fa5556d 100644
--- a/packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt
@@ -32,13 +32,11 @@
 import com.android.systemui.user.utils.UserScopedService
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapConcat
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.withContext
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class BuildNumberInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractor.kt
index 337fe1e..54f3d79 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/AuthRippleInteractor.kt
@@ -18,7 +18,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -26,7 +25,6 @@
 import kotlinx.coroutines.flow.merge
 
 /** Business logic for device entry auth ripple interactions. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class AuthRippleInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
index 9919f09..4edc5db 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.res.R
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
@@ -51,7 +50,6 @@
  * BiometricMessage business logic. Filters biometric error/fail/success events for authentication
  * events that should never surface a message to the user at the current device state.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class BiometricMessageInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
index 5699176..69da67e 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.emptyFlow
@@ -30,7 +29,6 @@
 import kotlinx.coroutines.flow.map
 
 /** Business logic for device entry biometric states that may differ based on the biometric mode. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DeviceEntryBiometricAuthInteractor
 @Inject
@@ -70,4 +68,10 @@
                 emptyFlow()
             }
         }
+
+    /** Triggered if a face failure occurs regardless of the mode. */
+    val faceFailure: Flow<FailedFaceAuthenticationStatus> =
+        deviceEntryFaceAuthInteractor.authenticationStatus.filterIsInstance<
+            FailedFaceAuthenticationStatus
+        >()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
index d495fac..bd80dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
@@ -20,12 +20,10 @@
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.shared.model.AuthenticationFlags
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 
 /** Encapsulates business logic for device entry biometric settings. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DeviceEntryBiometricSettingsInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt
index 7aee12f..5b03ccd 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.dagger.SysUISingleton
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
@@ -35,7 +34,6 @@
  * This class coordinates the lockout states of each individual biometric based on the lockout
  * states of other biometrics.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class DeviceEntryBiometricsAllowedInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
index cdd2b05..079d624 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
 import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.log.table.TableLogBuffer
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 
@@ -81,6 +82,8 @@
 
     /** Whether face auth is considered class 3 */
     fun isFaceAuthStrong(): Boolean
+
+    suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer)
 }
 
 /**
@@ -93,17 +96,17 @@
  */
 interface FaceAuthenticationListener {
     /** Receive face isAuthenticated updates */
-    fun onAuthenticatedChanged(isAuthenticated: Boolean)
+    fun onAuthenticatedChanged(isAuthenticated: Boolean) = Unit
 
     /** Receive face authentication status updates */
-    fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
+    fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus) = Unit
 
     /** Receive status updates whenever face detection runs */
-    fun onDetectionStatusChanged(status: FaceDetectionStatus)
+    fun onDetectionStatusChanged(status: FaceDetectionStatus) = Unit
 
-    fun onLockoutStateChanged(isLockedOut: Boolean)
+    fun onLockoutStateChanged(isLockedOut: Boolean) = Unit
 
-    fun onRunningStateChanged(isRunning: Boolean)
+    fun onRunningStateChanged(isRunning: Boolean) = Unit
 
-    fun onAuthEnrollmentStateChanged(enrolled: Boolean)
+    fun onAuthEnrollmentStateChanged(enrolled: Boolean) = Unit
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
index 5c058fe..db92e11 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
@@ -26,13 +26,11 @@
 import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.map
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class DeviceEntryFingerprintAuthInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
index ae62387..38e0503 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.combineTransform
@@ -44,7 +43,6 @@
  * particular, there are extra guards for whether device entry error and successes haptics should
  * play when the physical fingerprint sensor is located on the power button.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DeviceEntryHapticsInteractor
 @Inject
@@ -125,7 +123,7 @@
     private val playErrorHapticForBiometricFailure: Flow<Unit> =
         merge(
                 deviceEntryFingerprintAuthInteractor.fingerprintFailure,
-                deviceEntryBiometricAuthInteractor.faceOnlyFaceFailure,
+                deviceEntryBiometricAuthInteractor.faceFailure,
             )
             // map to Unit
             .map {}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 2e1096f..5b68597 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -25,7 +25,10 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
 import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.scene.data.model.asIterable
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
 import com.android.systemui.scene.domain.interactor.SceneBackInteractor
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
@@ -33,10 +36,11 @@
 import com.android.systemui.utils.coroutines.flow.mapLatestConflated
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
@@ -44,6 +48,7 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /**
  * Hosts application business logic related to device entry.
@@ -51,7 +56,6 @@
  * Device entry occurs when the user successfully dismisses (or bypasses) the lockscreen, regardless
  * of the authentication method used.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DeviceEntryInteractor
 @Inject
@@ -64,6 +68,7 @@
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
     private val dismissCallbackRegistry: DismissCallbackRegistry,
     sceneBackInteractor: SceneBackInteractor,
+    @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
 ) {
     /**
      * Whether the device is unlocked.
@@ -149,6 +154,11 @@
             ) { enteredDirectly, enteredOnBackStack ->
                 enteredOnBackStack || enteredDirectly
             }
+            .logDiffsForTable(
+                tableLogBuffer = tableLogBuffer,
+                columnName = "isDeviceEntered",
+                initialValue = false,
+            )
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
@@ -186,6 +196,11 @@
                         deviceUnlockStatus.deviceUnlockSource?.dismissesLockscreen == false)) &&
                     !isDeviceEntered
             }
+            .logDiffsForTable(
+                tableLogBuffer = tableLogBuffer,
+                columnName = "canSwipeToEnter",
+                initialValue = false,
+            )
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
@@ -273,4 +288,29 @@
     fun lockNow() {
         deviceUnlockedInteractor.lockNow()
     }
+
+    suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+        coroutineScope {
+            launch {
+                isDeviceEntered
+                    .logDiffsForTable(
+                        tableLogBuffer = tableLogBuffer,
+                        columnName = "isDeviceEntered",
+                        initialValue = isDeviceEntered.value,
+                    )
+                    .collect()
+            }
+
+            launch {
+                canSwipeToEnter
+                    .map { it?.toString() ?: "" }
+                    .logDiffsForTable(
+                        tableLogBuffer = tableLogBuffer,
+                        columnName = "canSwipeToEnter",
+                        initialValue = canSwipeToEnter.value?.toString() ?: "",
+                    )
+                    .collect()
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt
index b2d4405..eb82c7d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt
@@ -49,7 +49,6 @@
 import com.android.systemui.util.kotlin.combine
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.combine
@@ -68,7 +67,6 @@
  * bypass biometric or, if the device is already unlocked, by triggering an affordance that
  * dismisses the lockscreen.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DeviceEntrySourceInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
index 911327a..8ff8947 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.shared.customization.data.SensorLocation
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
@@ -30,7 +29,6 @@
 import kotlinx.coroutines.flow.flowOf
 
 /** Encapsulates business logic for device entry under-display fingerprint state changes. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DeviceEntryUdfpsInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
index 5e3b2ae..b1be9a2 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
@@ -33,15 +33,17 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.TrustInteractor
 import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
 import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import javax.inject.Inject
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.coroutineScope
@@ -49,6 +51,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -61,7 +64,6 @@
 import kotlinx.coroutines.flow.receiveAsFlow
 import kotlinx.coroutines.launch
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class DeviceUnlockedInteractor
 @Inject
@@ -76,6 +78,7 @@
     private val systemPropertiesHelper: SystemPropertiesHelper,
     private val userAwareSecureSettingsRepository: UserAwareSecureSettingsRepository,
     private val keyguardInteractor: KeyguardInteractor,
+    @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
 ) : ExclusiveActivatable() {
 
     private val deviceUnlockSource =
@@ -181,17 +184,33 @@
     private val lockNowRequests = Channel<Unit>()
 
     override suspend fun onActivated(): Nothing {
-        authenticationInteractor.authenticationMethod.collectLatest { authMethod ->
-            if (!authMethod.isSecure) {
-                // Device remains unlocked as long as the authentication method is not secure.
-                Log.d(TAG, "remaining unlocked because auth method not secure")
-                repository.deviceUnlockStatus.value = DeviceUnlockStatus(true, null)
-            } else if (authMethod == AuthenticationMethodModel.Sim) {
-                // Device remains locked while SIM is locked.
-                Log.d(TAG, "remaining locked because SIM locked")
-                repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null)
-            } else {
-                handleLockAndUnlockEvents()
+        coroutineScope {
+            launch {
+                authenticationInteractor.authenticationMethod.collectLatest { authMethod ->
+                    if (!authMethod.isSecure) {
+                        // Device remains unlocked as long as the authentication method is not
+                        // secure.
+                        Log.d(TAG, "remaining unlocked because auth method not secure")
+                        repository.deviceUnlockStatus.value = DeviceUnlockStatus(true, null)
+                    } else if (authMethod == AuthenticationMethodModel.Sim) {
+                        // Device remains locked while SIM is locked.
+                        Log.d(TAG, "remaining locked because SIM locked")
+                        repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null)
+                    } else {
+                        handleLockAndUnlockEvents()
+                    }
+                }
+            }
+
+            launch {
+                deviceUnlockStatus
+                    .map { it.isUnlocked }
+                    .logDiffsForTable(
+                        tableLogBuffer = tableLogBuffer,
+                        columnName = "isUnlocked",
+                        initialValue = deviceUnlockStatus.value.isUnlocked,
+                    )
+                    .collect()
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
index e81164b..7623e53 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filterIsInstance
@@ -37,7 +36,6 @@
  * events to determine whether a face auth event should be displayed to the user immediately or when
  * a [FaceManager.FACE_ERROR_TIMEOUT] is received.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class FaceHelpMessageDeferralInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
index 9b8c2b1..ecc4dbc 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
 import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.log.table.TableLogBuffer
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -73,4 +74,6 @@
     override fun onWalletLaunched() = Unit
 
     override fun onDeviceUnfolded() {}
+
+    override suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt
index 15631f8..e416b92 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt
@@ -40,7 +40,6 @@
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -52,7 +51,6 @@
 import kotlinx.coroutines.flow.map
 
 /** Business logic for handling authentication events when an app is occluding the lockscreen. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class OccludingAppDeviceEntryInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index b19b2d9..4b90e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -44,6 +44,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -53,13 +55,16 @@
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
@@ -379,6 +384,27 @@
             .launchIn(applicationScope)
     }
 
+    override suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+        conflatedCallbackFlow {
+                val listener =
+                    object : FaceAuthenticationListener {
+                        override fun onAuthEnrollmentStateChanged(enrolled: Boolean) {
+                            trySend(isFaceAuthEnabledAndEnrolled())
+                        }
+                    }
+
+                registerListener(listener)
+
+                awaitClose { unregisterListener(listener) }
+            }
+            .logDiffsForTable(
+                tableLogBuffer = tableLogBuffer,
+                columnName = "isFaceAuthEnabledAndEnrolled",
+                initialValue = isFaceAuthEnabledAndEnrolled(),
+            )
+            .collect()
+    }
+
     companion object {
         const val TAG = "DeviceEntryFaceAuthInteractor"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/UdfpsAccessibilityOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/UdfpsAccessibilityOverlayBinder.kt
index ef2537c..e2172d0 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/UdfpsAccessibilityOverlayBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/UdfpsAccessibilityOverlayBinder.kt
@@ -24,9 +24,7 @@
 import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
 import com.android.systemui.deviceentry.ui.viewmodel.UdfpsAccessibilityOverlayViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-@ExperimentalCoroutinesApi
 object UdfpsAccessibilityOverlayBinder {
 
     /** Forwards hover events to the view model to make guided announcements for accessibility. */
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt
index eeac527..5c7cd5f 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt
@@ -19,12 +19,10 @@
 import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
 
 /** Models the UI state for the alternate bouncer UDFPS accessibility overlay */
-@ExperimentalCoroutinesApi
 class AlternateBouncerUdfpsAccessibilityOverlayViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/DeviceEntryUdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/DeviceEntryUdfpsAccessibilityOverlayViewModel.kt
index af51576..b84d65a 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/DeviceEntryUdfpsAccessibilityOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/DeviceEntryUdfpsAccessibilityOverlayViewModel.kt
@@ -22,12 +22,10 @@
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 
 /** Models the UI state for the non-alternate bouncer UDFPS accessibility overlay */
-@ExperimentalCoroutinesApi
 class DeviceEntryUdfpsAccessibilityOverlayViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
index 191d612..fa849bf 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
@@ -23,14 +23,12 @@
 import com.android.systemui.biometrics.UdfpsUtils
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 
 /** Models the UI state for the UDFPS accessibility overlay */
-@ExperimentalCoroutinesApi
 abstract class UdfpsAccessibilityOverlayViewModel(
     udfpsOverlayInteractor: UdfpsOverlayInteractor,
     accessibilityInteractor: AccessibilityInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index faab31e..15f73ee 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -48,6 +48,8 @@
 import com.android.systemui.res.R;
 import com.android.systemui.touch.TouchInsetManager;
 
+import com.google.android.systemui.lowlightclock.LowLightClockDreamService;
+
 import dagger.Binds;
 import dagger.BindsOptionalOf;
 import dagger.Module;
@@ -238,15 +240,24 @@
     ComponentName bindsLowLightClockDream();
 
     /**
+     * Provides low light clock dream service component.
+     */
+    @Provides
+    @Named(LOW_LIGHT_CLOCK_DREAM)
+    static ComponentName providesLowLightClockDream(Context context) {
+        return new ComponentName(context, LowLightClockDreamService.class);
+    }
+
+    /**
      * Provides the component name of the low light dream, or null if not configured.
      */
     @Provides
     @Nullable
     @Named(LOW_LIGHT_DREAM_SERVICE)
     static ComponentName providesLowLightDreamService(Context context,
-            @Named(LOW_LIGHT_CLOCK_DREAM) Optional<ComponentName> clockDream) {
-        if (Flags.lowLightClockDream() && clockDream.isPresent()) {
-            return clockDream.get();
+            @Named(LOW_LIGHT_CLOCK_DREAM) ComponentName clockDream) {
+        if (Flags.lowLightClockDream()) {
+            return clockDream;
         }
 
         String lowLightDreamComponent = context.getResources().getString(
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegator.kt
index b14903d..7938889 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegator.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegator.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.util.service.PersistentConnectionManager
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -45,7 +44,6 @@
  * Queries a remote service for [HomeControlsComponentInfo] necessary to show the home controls
  * dream.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class RemoteHomeControlsDataSourceDelegator
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractor.kt
index 31bd708..26aa021 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractor.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -42,7 +41,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 @SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
 class HomeControlsComponentInteractor
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index 5c0335a6..11b7e9d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
@@ -34,14 +35,12 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.kotlin.FlowDumperImpl
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.merge
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class DreamViewModel
 @Inject
@@ -53,6 +52,7 @@
     private val toLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
     private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor,
     private val communalInteractor: CommunalInteractor,
+    private val communalSettingsInteractor: CommunalSettingsInteractor,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val userTracker: UserTracker,
     dumpManager: DumpManager,
@@ -60,8 +60,12 @@
 
     fun startTransitionFromDream() {
         val showGlanceableHub =
-            communalInteractor.isCommunalEnabled.value &&
-                !keyguardUpdateMonitor.isEncryptedOrLockdown(userTracker.userId)
+            if (communalSettingsInteractor.isV2FlagEnabled()) {
+                communalInteractor.shouldShowCommunal.value
+            } else {
+                communalInteractor.isCommunalEnabled.value &&
+                    !keyguardUpdateMonitor.isEncryptedOrLockdown(userTracker.userId)
+            }
         fromDreamingTransitionInteractor.startToLockscreenOrGlanceableHubTransition(
             showGlanceableHub && !glanceableHubAllowKeyguardWhenDreaming()
         )
diff --git a/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt b/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
index 9596a54..0640351 100644
--- a/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
@@ -113,7 +113,6 @@
 
     private val datastore = MutableStateFlow<DataStore<Preferences>?>(null)
 
-    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
     private val prefData: Flow<Preferences> = datastore.filterNotNull().flatMapLatest { it.data }
 
     override val keyboardShortcutTriggered: Flow<GestureType> =
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
index d7a4dba..685150f 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
@@ -37,8 +37,8 @@
 import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
 import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
 import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
-import com.android.systemui.recents.OverviewProxyService
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import java.time.Clock
 import java.time.Instant
@@ -48,7 +48,6 @@
 import kotlin.time.DurationUnit
 import kotlin.time.toDuration
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -67,7 +66,7 @@
     private val contextualEducationInteractor: ContextualEducationInteractor,
     private val userInputDeviceRepository: UserInputDeviceRepository,
     private val tutorialRepository: TutorialSchedulerRepository,
-    private val overviewProxyService: OverviewProxyService,
+    private val launcherProxyService: LauncherProxyService,
     private val metricsLogger: ContextualEducationMetricsLogger,
     @EduClock private val clock: Clock,
 ) : CoreStartable {
@@ -100,8 +99,8 @@
     val educationTriggered = _educationTriggered.asStateFlow()
 
     private val statsUpdateRequests: Flow<StatsUpdateRequest> = conflatedCallbackFlow {
-        val listener: OverviewProxyListener =
-            object : OverviewProxyListener {
+        val listener: LauncherProxyListener =
+            object : LauncherProxyListener {
                 override fun updateContextualEduStats(
                     isTrackpadGesture: Boolean,
                     gestureType: GestureType,
@@ -113,8 +112,8 @@
                 }
             }
 
-        overviewProxyService.addCallback(listener)
-        awaitClose { overviewProxyService.removeCallback(listener) }
+        launcherProxyService.addCallback(listener)
+        awaitClose { launcherProxyService.removeCallback(listener) }
     }
 
     private val gestureModelMap: Flow<Map<GestureType, GestureEduModel>> =
@@ -127,7 +126,6 @@
             mapOf(BACK to back, HOME to home, OVERVIEW to overview, ALL_APPS to allApps)
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     override fun start() {
         backgroundScope.launch {
             contextualEducationInteractor.eduDeviceConnectionTimeFlow
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
index 443ad020..e1113ec 100644
--- a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
@@ -37,7 +37,6 @@
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.map
 
-@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class ContextualEduViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index e588077..200a751 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiAod
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
 import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
@@ -55,8 +54,6 @@
         NotificationMinimalism.token dependsOn NotificationThrottleHun.token
         ModesEmptyShadeFix.token dependsOn modesUi
 
-        PromotedNotificationUiForceExpanded.token dependsOn PromotedNotificationUi.token
-
         PromotedNotificationUiAod.token dependsOn PromotedNotificationUi.token
 
         // SceneContainer dependencies
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2c33c0b..d9d3995 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -206,9 +206,6 @@
     // TODO(b/267007629): Tracking Bug
     val MEDIA_RESUME_PROGRESS = releasedFlag("media_resume_progress")
 
-    // TODO(b/267166152) : Tracking Bug
-    val MEDIA_RETAIN_RECOMMENDATIONS = unreleasedFlag("media_retain_recommendations")
-
     // TODO(b/270437894): Tracking Bug
     val MEDIA_REMOTE_RESUME = unreleasedFlag("media_remote_resume")
 
diff --git a/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt b/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt
index 62ab18b..96ef03c 100644
--- a/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt
+++ b/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.key
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
@@ -63,6 +64,7 @@
     rowSpacing: Dp,
     spans: List<Int>,
     modifier: Modifier = Modifier,
+    keys: (spanIndex: Int) -> Any = { it },
     composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
 ) {
     SpannedGrid(
@@ -72,6 +74,7 @@
         spans = spans,
         isVertical = false,
         modifier = modifier,
+        keys = keys,
         composables = composables,
     )
 }
@@ -103,6 +106,7 @@
     rowSpacing: Dp,
     spans: List<Int>,
     modifier: Modifier = Modifier,
+    keys: (spanIndex: Int) -> Any = { it },
     composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
 ) {
     SpannedGrid(
@@ -112,6 +116,7 @@
         spans = spans,
         isVertical = true,
         modifier = modifier,
+        keys = keys,
         composables = composables,
     )
 }
@@ -124,6 +129,7 @@
     spans: List<Int>,
     isVertical: Boolean,
     modifier: Modifier = Modifier,
+    keys: (spanIndex: Int) -> Any = { it },
     composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
 ) {
     val crossAxisArrangement = Arrangement.spacedBy(crossAxisSpacing)
@@ -167,17 +173,19 @@
     Layout(
         {
             (0 until spans.size).map { spanIndex ->
-                Box(
-                    Modifier.semantics {
-                        collectionItemInfo =
-                            if (isVertical) {
-                                CollectionItemInfo(spanIndex, 1, 0, 1)
-                            } else {
-                                CollectionItemInfo(0, 1, spanIndex, 1)
-                            }
+                key(keys(spanIndex)) {
+                    Box(
+                        Modifier.semantics {
+                            collectionItemInfo =
+                                if (isVertical) {
+                                    CollectionItemInfo(spanIndex, 1, 0, 1)
+                                } else {
+                                    CollectionItemInfo(0, 1, spanIndex, 1)
+                                }
+                        }
+                    ) {
+                        composables(spanIndex)
                     }
-                ) {
-                    composables(spanIndex)
                 }
             }
         },
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
index 84c4bdf..70a40d9 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.haptics.msdl.qs
 
 import android.service.quicksettings.Tile
+import androidx.compose.runtime.Stable
 import com.android.systemui.Flags
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.SysUISingleton
@@ -29,7 +30,6 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -40,7 +40,6 @@
 import kotlinx.coroutines.flow.transform
 
 /** A view-model to trigger haptic feedback on Quick Settings tiles */
-@OptIn(ExperimentalCoroutinesApi::class)
 class TileHapticsViewModel
 @AssistedInject
 constructor(
@@ -175,6 +174,7 @@
 }
 
 @SysUISingleton
+@Stable
 class TileHapticsViewModelFactoryProvider
 @Inject
 constructor(private val tileHapticsViewModelFactory: TileHapticsViewModel.Factory) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt
index df2a33f..edf84b2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.keyboard.data.repository.KeyboardRepository
 import com.android.systemui.keyboard.shared.model.BacklightModel
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
@@ -35,7 +34,6 @@
 ) {
 
     /** Emits current backlight level as [BacklightModel] or null if keyboard is not connected */
-    @ExperimentalCoroutinesApi
     val backlight: Flow<BacklightModel?> =
         keyboardRepository.isAnyKeyboardConnected.flatMapLatest { connected ->
             if (connected) keyboardRepository.backlight else flowOf(null)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
index e5c638c..d355f76 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
@@ -32,18 +32,19 @@
 import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.ERROR_RESERVED_COMBINATION
 import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.SUCCESS
 import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.withContext
-import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
 
 @SysUISingleton
 class CustomInputGesturesRepository
 @Inject
-constructor(private val userTracker: UserTracker,
-    @Background private val bgCoroutineContext: CoroutineContext)
-{
+constructor(
+    private val userTracker: UserTracker,
+    @Background private val bgCoroutineContext: CoroutineContext,
+) {
 
     private val userContext: Context
         get() = userTracker.createCurrentUserContext(userTracker.userContext)
@@ -55,8 +56,7 @@
 
     private val _customInputGesture = MutableStateFlow<List<InputGestureData>>(emptyList())
 
-    val customInputGestures =
-        _customInputGesture.onStart { refreshCustomInputGestures() }
+    val customInputGestures = _customInputGesture.onStart { refreshCustomInputGestures() }
 
     fun refreshCustomInputGestures() {
         setCustomInputGestures(inputGestures = retrieveCustomInputGestures())
@@ -72,24 +72,24 @@
         } else emptyList()
     }
 
-    suspend fun addCustomInputGesture(inputGesture: InputGestureData): ShortcutCustomizationRequestResult {
+    suspend fun addCustomInputGesture(
+        inputGesture: InputGestureData
+    ): ShortcutCustomizationRequestResult {
         return withContext(bgCoroutineContext) {
             when (val result = inputManager.addCustomInputGesture(inputGesture)) {
                 CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> {
                     refreshCustomInputGestures()
                     SUCCESS
                 }
-                CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS ->
-                    ERROR_RESERVED_COMBINATION
+                CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS -> ERROR_RESERVED_COMBINATION
 
-                CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE ->
-                    ERROR_RESERVED_COMBINATION
+                CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE -> ERROR_RESERVED_COMBINATION
 
                 else -> {
                     Log.w(
                         TAG,
                         "Attempted to add inputGesture: $inputGesture " +
-                                "but ran into an error with code: $result",
+                            "but ran into an error with code: $result",
                     )
                     ERROR_OTHER
                 }
@@ -97,11 +97,11 @@
         }
     }
 
-    suspend fun deleteCustomInputGesture(inputGesture: InputGestureData): ShortcutCustomizationRequestResult {
-        return withContext(bgCoroutineContext){
-            when (
-                val result = inputManager.removeCustomInputGesture(inputGesture)
-            ) {
+    suspend fun deleteCustomInputGesture(
+        inputGesture: InputGestureData
+    ): ShortcutCustomizationRequestResult {
+        return withContext(bgCoroutineContext) {
+            when (val result = inputManager.removeCustomInputGesture(inputGesture)) {
                 CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> {
                     refreshCustomInputGestures()
                     SUCCESS
@@ -110,7 +110,7 @@
                     Log.w(
                         TAG,
                         "Attempted to delete inputGesture: $inputGesture " +
-                                "but ran into an error with code: $result",
+                            "but ran into an error with code: $result",
                     )
                     ERROR_OTHER
                 }
@@ -134,7 +134,10 @@
         }
     }
 
+    suspend fun getInputGestureByTrigger(trigger: InputGestureData.Trigger): InputGestureData? =
+        withContext(bgCoroutineContext) { inputManager.getInputGesture(trigger) }
+
     private companion object {
         private const val TAG = "CustomInputGesturesRepository"
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
index 18ca877..6ae948d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
@@ -18,6 +18,7 @@
 
 import android.hardware.input.InputGestureData
 import android.hardware.input.InputGestureData.Builder
+import android.hardware.input.InputGestureData.Trigger
 import android.hardware.input.InputGestureData.createKeyTrigger
 import android.hardware.input.InputManager
 import android.hardware.input.KeyGestureEvent.KeyGestureType
@@ -175,6 +176,11 @@
         return customInputGesturesRepository.resetAllCustomInputGestures()
     }
 
+    suspend fun isSelectedKeyCombinationAvailable(): Boolean {
+        val trigger = buildTriggerFromSelectedKeyCombination() ?: return false
+        return customInputGesturesRepository.getInputGestureByTrigger(trigger) == null
+    }
+
     private fun Builder.addKeyGestureTypeForShortcutBeingCustomized(): Builder {
         val keyGestureType = getKeyGestureTypeForShortcutBeingCustomized()
 
@@ -222,7 +228,10 @@
         )
     }
 
-    private fun Builder.addTriggerFromSelectedKeyCombination(): Builder {
+    private fun Builder.addTriggerFromSelectedKeyCombination(): Builder =
+        setTrigger(buildTriggerFromSelectedKeyCombination())
+
+    private fun buildTriggerFromSelectedKeyCombination(): Trigger? {
         val selectedKeyCombination = _selectedKeyCombination.value
         if (selectedKeyCombination?.keyCode == null) {
             Log.w(
@@ -230,16 +239,14 @@
                 "User requested to set shortcut but selected key combination is " +
                     "$selectedKeyCombination",
             )
-            return this
+            return null
         }
 
-        return setTrigger(
-            createKeyTrigger(
-                /* keycode = */ selectedKeyCombination.keyCode,
-                /* modifierState = */ shortcutCategoriesUtils.removeUnsupportedModifiers(
-                    selectedKeyCombination.modifiers
-                ),
-            )
+        return createKeyTrigger(
+            /* keycode= */ selectedKeyCombination.keyCode,
+            /* modifierState= */ shortcutCategoriesUtils.removeUnsupportedModifiers(
+                selectedKeyCombination.modifiers
+            ),
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
index 6a42cdc..0908e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyboard.shortcut.data.repository
 
 import android.content.Context
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_BACK
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
@@ -39,10 +40,16 @@
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS
-import com.android.hardware.input.Flags.enableVoiceAccessKeyGestures
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.Accessibility
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
@@ -65,7 +72,6 @@
             KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to System,
             KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to System,
             KEY_GESTURE_TYPE_ALL_APPS to System,
-            KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to System,
 
             // Multitasking Category
             KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to MultiTasking,
@@ -82,6 +88,16 @@
 
             // App Category
             KEY_GESTURE_TYPE_LAUNCH_APPLICATION to AppCategories,
+
+            // Accessibility Category
+            KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS to Accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS to Accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS to Accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS to Accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to Accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_TALKBACK to Accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION to Accessibility,
+            KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK to Accessibility,
         )
 
     val gestureToInternalKeyboardShortcutGroupLabelResIdMap =
@@ -103,7 +119,6 @@
             KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to R.string.shortcut_helper_category_system_apps,
             KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to
                 R.string.shortcut_helper_category_system_apps,
-            KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to R.string.shortcut_helper_category_system_apps,
 
             // Multitasking Category
             KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to
@@ -128,6 +143,17 @@
 
             // App Category
             KEY_GESTURE_TYPE_LAUNCH_APPLICATION to R.string.keyboard_shortcut_group_applications,
+
+            // Accessibility Category
+            KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS to R.string.shortcutHelper_category_accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS to R.string.shortcutHelper_category_accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS to R.string.shortcutHelper_category_accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS to R.string.shortcutHelper_category_accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to R.string.shortcutHelper_category_accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_TALKBACK to R.string.shortcutHelper_category_accessibility,
+            KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION to R.string.shortcutHelper_category_accessibility,
+            KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK to
+                R.string.shortcutHelper_category_accessibility,
         )
 
     /**
@@ -152,7 +178,6 @@
             KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to R.string.group_system_access_google_assistant,
             KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to
                 R.string.group_system_access_google_assistant,
-            KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to R.string.group_system_toggle_voice_access,
 
             // Multitasking Category
             KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to R.string.group_system_cycle_forward,
@@ -169,6 +194,19 @@
                 R.string.system_desktop_mode_toggle_maximize_window,
             KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to
                 R.string.system_multitasking_move_to_next_display,
+
+            // Accessibility Category
+            KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS to R.string.group_accessibility_toggle_bounce_keys,
+            KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS to R.string.group_accessibility_toggle_mouse_keys,
+            KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS to R.string.group_accessibility_toggle_sticky_keys,
+            KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS to R.string.group_accessibility_toggle_slow_keys,
+            KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to
+                R.string.group_accessibility_toggle_voice_access,
+            KEY_GESTURE_TYPE_TOGGLE_TALKBACK to R.string.group_accessibility_toggle_talkback,
+            KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION to
+                R.string.group_accessibility_toggle_magnification,
+            KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK to
+                R.string.group_accessibility_activate_select_to_speak,
         )
 
     val shortcutLabelToKeyGestureTypeMap: Map<String, Int>
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/AccessibilityShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/AccessibilityShortcutsSource.kt
index d92c455..fdb80b2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/AccessibilityShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/AccessibilityShortcutsSource.kt
@@ -17,9 +17,24 @@
 package com.android.systemui.keyboard.shortcut.data.source
 
 import android.content.res.Resources
+import android.hardware.input.InputSettings
+import android.view.KeyEvent.KEYCODE_3
+import android.view.KeyEvent.KEYCODE_4
+import android.view.KeyEvent.KEYCODE_5
+import android.view.KeyEvent.KEYCODE_6
+import android.view.KeyEvent.KEYCODE_M
+import android.view.KeyEvent.KEYCODE_S
+import android.view.KeyEvent.KEYCODE_T
+import android.view.KeyEvent.KEYCODE_V
+import android.view.KeyEvent.META_ALT_ON
+import android.view.KeyEvent.META_META_ON
 import android.view.KeyboardShortcutGroup
 import android.view.KeyboardShortcutInfo
+import com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures
+import com.android.hardware.input.Flags.enableVoiceAccessKeyGestures
+import com.android.hardware.input.Flags.keyboardA11yShortcutControl
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo
 import com.android.systemui.res.R
 import javax.inject.Inject
 
@@ -33,5 +48,96 @@
             )
         )
 
-    private fun accessibilityShortcuts() = listOf<KeyboardShortcutInfo>()
+    private fun accessibilityShortcuts(): List<KeyboardShortcutInfo> {
+        val shortcuts = mutableListOf<KeyboardShortcutInfo>()
+
+        if (keyboardA11yShortcutControl()) {
+            if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()) {
+                shortcuts.add(
+                    // Toggle bounce keys:
+                    //  - Meta + Alt + 3
+                    shortcutInfo(
+                        resources.getString(R.string.group_accessibility_toggle_bounce_keys)
+                    ) {
+                        command(META_META_ON or META_ALT_ON, KEYCODE_3)
+                    }
+                )
+            }
+            if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()) {
+                shortcuts.add(
+                    // Toggle mouse keys:
+                    //  - Meta + Alt + 4
+                    shortcutInfo(
+                        resources.getString(R.string.group_accessibility_toggle_mouse_keys)
+                    ) {
+                        command(META_META_ON or META_ALT_ON, KEYCODE_4)
+                    }
+                )
+            }
+            if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()) {
+                shortcuts.add(
+                    // Toggle sticky keys:
+                    //  - Meta + Alt + 5
+                    shortcutInfo(
+                        resources.getString(R.string.group_accessibility_toggle_sticky_keys)
+                    ) {
+                        command(META_META_ON or META_ALT_ON, KEYCODE_5)
+                    }
+                )
+            }
+            if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()) {
+                shortcuts.add(
+                    // Toggle slow keys:
+                    //  - Meta + Alt + 6
+                    shortcutInfo(
+                        resources.getString(R.string.group_accessibility_toggle_slow_keys)
+                    ) {
+                        command(META_META_ON or META_ALT_ON, KEYCODE_6)
+                    }
+                )
+            }
+        }
+
+        if (enableVoiceAccessKeyGestures()) {
+            shortcuts.add(
+                // Toggle voice access:
+                //  - Meta + Alt + V
+                shortcutInfo(
+                    resources.getString(R.string.group_accessibility_toggle_voice_access)
+                ) {
+                    command(META_META_ON or META_ALT_ON, KEYCODE_V)
+                }
+            )
+        }
+
+        if (enableTalkbackAndMagnifierKeyGestures()) {
+            shortcuts.add(
+                // Toggle talkback:
+                //  - Meta + Alt + T
+                shortcutInfo(resources.getString(R.string.group_accessibility_toggle_talkback)) {
+                    command(META_META_ON or META_ALT_ON, KEYCODE_T)
+                }
+            )
+            shortcuts.add(
+                // Toggle magnification:
+                //  - Meta + Alt + M
+                shortcutInfo(
+                    resources.getString(R.string.group_accessibility_toggle_magnification)
+                ) {
+                    command(META_META_ON or META_ALT_ON, KEYCODE_M)
+                }
+            )
+            shortcuts.add(
+                // Activate Select to Speak:
+                //  - Meta + Alt + S
+                shortcutInfo(
+                    resources.getString(R.string.group_accessibility_activate_select_to_speak)
+                ) {
+                    command(META_META_ON or META_ALT_ON, KEYCODE_S)
+                }
+            )
+        }
+
+        return shortcuts
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
index d785b5b..464201f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
@@ -29,12 +29,12 @@
 import android.view.KeyEvent.META_CTRL_ON
 import android.view.KeyEvent.META_META_ON
 import android.view.KeyboardShortcutGroup
+import android.window.DesktopModeFlags
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo
 import com.android.systemui.res.R
 import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
-import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import javax.inject.Inject
 
@@ -85,7 +85,8 @@
             )
         }
         if (
-            DesktopModeStatus.canEnterDesktopMode(context) && enableTaskResizingKeyboardShortcuts()
+            DesktopModeStatus.canEnterDesktopMode(context) &&
+                DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue
         ) {
             // Snap a freeform window to the left
             //  - Meta + Left bracket
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
index c3c9df9..8bed853 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
@@ -32,14 +32,12 @@
 import android.view.KeyEvent.KEYCODE_S
 import android.view.KeyEvent.KEYCODE_SLASH
 import android.view.KeyEvent.KEYCODE_TAB
-import android.view.KeyEvent.KEYCODE_V
 import android.view.KeyEvent.META_ALT_ON
 import android.view.KeyEvent.META_CTRL_ON
 import android.view.KeyEvent.META_META_ON
 import android.view.KeyEvent.META_SHIFT_ON
 import android.view.KeyboardShortcutGroup
 import android.view.KeyboardShortcutInfo
-import com.android.hardware.input.Flags.enableVoiceAccessKeyGestures
 import com.android.systemui.Flags.shortcutHelperKeyGlyph
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo
@@ -120,8 +118,8 @@
         return shortcuts
     }
 
-    private fun systemControlsShortcuts(): List<KeyboardShortcutInfo>  {
-        val shortcuts = mutableListOf(
+    private fun systemControlsShortcuts() =
+        listOf(
             // Access list of all apps and search (i.e. Search/Launcher):
             //  - Meta
             shortcutInfo(resources.getString(R.string.group_system_access_all_apps_search)) {
@@ -157,9 +155,9 @@
                 command(META_META_ON, KEYCODE_DPAD_LEFT)
             },
             // Take a full screenshot:
-            //  - Meta + Ctrl + S
+            //  - Meta + S
             shortcutInfo(resources.getString(R.string.group_system_full_screenshot)) {
-                command(META_META_ON or META_CTRL_ON, KEYCODE_S)
+                command(META_META_ON, KEYCODE_S)
             },
             // Access list of system / apps shortcuts:
             //  - Meta + /
@@ -178,19 +176,6 @@
             },
         )
 
-        if (enableVoiceAccessKeyGestures()) {
-            shortcuts.add(
-                // Toggle voice access:
-                //  - Meta + Alt + V
-                shortcutInfo(resources.getString(R.string.group_system_toggle_voice_access)) {
-                    command(META_META_ON or META_ALT_ON, KEYCODE_V)
-                }
-            )
-        }
-
-        return shortcuts
-    }
-
     private fun systemAppsShortcuts() =
         listOf(
             // Pull up Notes app for quick memo:
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
index ef24267..1a62517 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
@@ -53,4 +53,7 @@
     suspend fun resetAllCustomShortcuts(): ShortcutCustomizationRequestResult {
         return customShortcutRepository.resetAllCustomShortcuts()
     }
+
+    suspend fun isSelectedKeyCombinationAvailable(): Boolean =
+        customShortcutRepository.isSelectedKeyCombinationAvailable()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
index f1945e6..54e27a6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
@@ -43,6 +43,7 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 
 class ShortcutCustomizationDialogStarter
@@ -57,20 +58,25 @@
     private val viewModel = viewModelFactory.create()
 
     override suspend fun onActivated(): Nothing {
-        viewModel.shortcutCustomizationUiState.collect { uiState ->
-            when (uiState) {
-                is AddShortcutDialog,
-                is DeleteShortcutDialog,
-                is ResetShortcutDialog -> {
-                    if (dialog == null) {
-                        dialog = createDialog().also { it.show() }
+        coroutineScope {
+            launch {
+                viewModel.shortcutCustomizationUiState.collect { uiState ->
+                    when (uiState) {
+                        is AddShortcutDialog,
+                        is DeleteShortcutDialog,
+                        is ResetShortcutDialog -> {
+                            if (dialog == null) {
+                                dialog = createDialog().also { it.show() }
+                            }
+                        }
+                        is ShortcutCustomizationUiState.Inactive -> {
+                            dialog?.dismiss()
+                            dialog = null
+                        }
                     }
                 }
-                is ShortcutCustomizationUiState.Inactive -> {
-                    dialog?.dismiss()
-                    dialog = null
-                }
             }
+            launch { viewModel.activate() }
         }
         awaitCancellation()
     }
@@ -101,6 +107,7 @@
                 onConfirmResetShortcut = {
                     coroutineScope.launch { viewModel.resetAllCustomShortcuts() }
                 },
+                onClearSelectedKeyCombination = { viewModel.clearSelectedKeyCombination() },
             )
             setDialogProperties(dialog, uiState)
         }
@@ -108,22 +115,33 @@
 
     private fun setDialogProperties(dialog: SystemUIDialog, uiState: ShortcutCustomizationUiState) {
         dialog.setOnDismissListener { viewModel.onDialogDismissed() }
-        dialog.setTitle(
-            resources.getString(
-                when (uiState) {
-                    is AddShortcutDialog ->
-                        R.string.shortcut_customize_mode_add_shortcut_description
-                    is DeleteShortcutDialog ->
-                        R.string.shortcut_customize_mode_remove_shortcut_description
-                    else -> R.string.shortcut_customize_mode_reset_shortcut_description
-                }
-            )
-        )
+        dialog.setTitle("${getDialogTitle(uiState)}. ${getDialogDescription(uiState)}")
         // By default, apps cannot intercept action key. The system always handles it. This
         // flag is needed to enable customisation dialog window to intercept action key
         dialog.window?.addPrivateFlags(PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS)
     }
 
+    private fun getDialogTitle(uiState: ShortcutCustomizationUiState): String {
+        return when (uiState) {
+            is AddShortcutDialog -> uiState.shortcutLabel
+            is DeleteShortcutDialog ->
+                resources.getString(R.string.shortcut_customize_mode_remove_shortcut_dialog_title)
+            else ->
+                resources.getString(R.string.shortcut_customize_mode_reset_shortcut_dialog_title)
+        }
+    }
+
+    private fun getDialogDescription(uiState: ShortcutCustomizationUiState): String {
+        return resources.getString(
+            when (uiState) {
+                is AddShortcutDialog -> R.string.shortcut_customize_mode_add_shortcut_description
+                is DeleteShortcutDialog ->
+                    R.string.shortcut_customize_mode_remove_shortcut_description
+                else -> R.string.shortcut_customize_mode_reset_shortcut_description
+            }
+        )
+    }
+
     @AssistedFactory
     interface Factory {
         fun create(): ShortcutCustomizationDialogStarter
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
index fa03883..ea36a10 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
@@ -24,7 +24,6 @@
 import android.provider.Settings
 import androidx.annotation.VisibleForTesting
 import androidx.compose.foundation.layout.width
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
@@ -36,6 +35,7 @@
 import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelperBottomSheet
 import com.android.systemui.keyboard.shortcut.ui.composable.getWidth
 import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
+import com.android.systemui.lifecycle.rememberActivated
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialogFactory
@@ -51,14 +51,13 @@
 constructor(
     @Application private val applicationScope: CoroutineScope,
     private val shortcutHelperViewModel: ShortcutHelperViewModel,
-    shortcutCustomizationDialogStarterFactory: ShortcutCustomizationDialogStarter.Factory,
+    private val shortcutCustomizationDialogStarterFactory:
+        ShortcutCustomizationDialogStarter.Factory,
     private val dialogFactory: SystemUIDialogFactory,
     private val activityStarter: ActivityStarter,
 ) : CoreStartable {
 
     @VisibleForTesting var dialog: Dialog? = null
-    private val shortcutCustomizationDialogStarter =
-        shortcutCustomizationDialogStarterFactory.create()
 
     override fun start() {
         shortcutHelperViewModel.shouldShow
@@ -77,7 +76,10 @@
             content = { dialog ->
                 val shortcutsUiState by
                     shortcutHelperViewModel.shortcutsUiState.collectAsStateWithLifecycle()
-                LaunchedEffect(Unit) { shortcutCustomizationDialogStarter.activate() }
+                val shortcutCustomizationDialogStarter =
+                    rememberActivated(traceName = "shortcutCustomizationDialogStarter") {
+                        shortcutCustomizationDialogStarterFactory.create()
+                    }
                 ShortcutHelper(
                     modifier = Modifier.width(getWidth()),
                     shortcutsUiState = shortcutsUiState,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
index 550438a..66e4505 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
@@ -18,14 +18,10 @@
 
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.interaction.collectIsFocusedAsState
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.heightIn
@@ -40,13 +36,15 @@
 import androidx.compose.material.icons.filled.ErrorOutline
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusDirection
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusProperties
 import androidx.compose.ui.focus.focusRequester
@@ -57,9 +55,15 @@
 import androidx.compose.ui.input.key.key
 import androidx.compose.ui.input.key.onPreviewKeyEvent
 import androidx.compose.ui.input.key.type
+import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.LiveRegionMode
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.liveRegion
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import com.android.compose.ui.graphics.painter.rememberDrawablePainter
@@ -76,6 +80,7 @@
     onConfirmSetShortcut: () -> Unit,
     onConfirmDeleteShortcut: () -> Unit,
     onConfirmResetShortcut: () -> Unit,
+    onClearSelectedKeyCombination: () -> Unit,
 ) {
     when (uiState) {
         is ShortcutCustomizationUiState.AddShortcutDialog -> {
@@ -85,6 +90,7 @@
                 onShortcutKeyCombinationSelected,
                 onCancel,
                 onConfirmSetShortcut,
+                onClearSelectedKeyCombination,
             )
         }
         is ShortcutCustomizationUiState.DeleteShortcutDialog -> {
@@ -106,6 +112,7 @@
     onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean,
     onCancel: () -> Unit,
     onConfirmSetShortcut: () -> Unit,
+    onClearSelectedKeyCombination: () -> Unit,
 ) {
     Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
         Title(uiState.shortcutLabel)
@@ -121,6 +128,7 @@
             onShortcutKeyCombinationSelected = onShortcutKeyCombinationSelected,
             pressedKeys = uiState.pressedKeys,
             onConfirmSetShortcut = onConfirmSetShortcut,
+            onClearSelectedKeyCombination = onClearSelectedKeyCombination,
         )
         ErrorMessageContainer(uiState.errorMessage)
         DialogButtons(
@@ -244,7 +252,11 @@
                 lineHeight = 20.sp,
                 fontWeight = FontWeight.W500,
                 color = MaterialTheme.colorScheme.error,
-                modifier = Modifier.padding(start = 24.dp).width(252.dp),
+                modifier =
+                    Modifier.padding(start = 24.dp).width(252.dp).semantics {
+                        contentDescription = errorMessage
+                        liveRegion = LiveRegionMode.Polite
+                    },
             )
         }
     }
@@ -256,72 +268,82 @@
     onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean,
     pressedKeys: List<ShortcutKey>,
     onConfirmSetShortcut: () -> Unit,
+    onClearSelectedKeyCombination: () -> Unit,
 ) {
-    val interactionSource = remember { MutableInteractionSource() }
-    val isFocused by interactionSource.collectIsFocusedAsState()
-    val outlineColor =
-        if (!isFocused) MaterialTheme.colorScheme.outline
-        else if (shouldShowError) MaterialTheme.colorScheme.error
-        else MaterialTheme.colorScheme.primary
     val focusRequester = remember { FocusRequester() }
-
+    val focusManager = LocalFocusManager.current
     LaunchedEffect(Unit) { focusRequester.requestFocus() }
 
-    ClickableShortcutSurface(
-        onClick = {},
-        color = Color.Transparent,
-        shape = RoundedCornerShape(50.dp),
+    OutlinedInputField(
         modifier =
             Modifier.padding(all = 16.dp)
                 .sizeIn(minWidth = 332.dp, minHeight = 56.dp)
-                .border(width = 2.dp, color = outlineColor, shape = RoundedCornerShape(50.dp))
+                .focusRequester(focusRequester)
+                .focusProperties { canFocus = true }
                 .onPreviewKeyEvent { keyEvent ->
                     val keyEventProcessed = onShortcutKeyCombinationSelected(keyEvent)
-                    if (
-                        !keyEventProcessed &&
-                            keyEvent.key == Key.Enter &&
-                            keyEvent.type == KeyEventType.KeyUp
-                    ) {
-                        onConfirmSetShortcut()
+                    if (keyEventProcessed) {
                         true
-                    } else keyEventProcessed
-                }
-                .focusProperties { canFocus = true } // enables keyboard focus when in touch mode
-                .focusRequester(focusRequester),
-        interactionSource = interactionSource,
-    ) {
-        Row(
-            modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 16.dp, bottom = 16.dp),
-            verticalAlignment = Alignment.CenterVertically,
-        ) {
-            if (pressedKeys.isEmpty()) {
-                PressKeyPrompt()
+                    } else {
+                        if (keyEvent.type == KeyEventType.KeyUp) {
+                            when (keyEvent.key) {
+                                Key.Enter -> {
+                                    onConfirmSetShortcut()
+                                    return@onPreviewKeyEvent true
+                                }
+                                Key.Backspace -> {
+                                    onClearSelectedKeyCombination()
+                                    return@onPreviewKeyEvent true
+                                }
+                                Key.DirectionDown -> {
+                                    focusManager.moveFocus(FocusDirection.Down)
+                                    return@onPreviewKeyEvent true
+                                }
+                                else -> return@onPreviewKeyEvent false
+                            }
+                        } else false
+                    }
+                },
+        trailingIcon = { ErrorIcon(shouldShowError) },
+        isError = shouldShowError,
+        placeholder = { PressKeyPrompt() },
+        content =
+            if (pressedKeys.isNotEmpty()) {
+                { PressedKeysTextContainer(pressedKeys) }
             } else {
-                PressedKeysTextContainer(pressedKeys)
-            }
-            Spacer(modifier = Modifier.weight(1f))
-            if (shouldShowError) {
-                Icon(
-                    imageVector = Icons.Default.ErrorOutline,
-                    contentDescription = null,
-                    modifier = Modifier.size(20.dp),
-                    tint = MaterialTheme.colorScheme.error,
-                )
-            }
-        }
+                null
+            },
+    )
+}
+
+@Composable
+private fun ErrorIcon(shouldShowError: Boolean) {
+    if (shouldShowError) {
+        Icon(
+            imageVector = Icons.Default.ErrorOutline,
+            contentDescription = null,
+            modifier = Modifier.size(20.dp),
+            tint = MaterialTheme.colorScheme.error,
+        )
     }
 }
 
 @Composable
-private fun RowScope.PressedKeysTextContainer(pressedKeys: List<ShortcutKey>) {
-    pressedKeys.forEachIndexed { keyIndex, key ->
-        if (keyIndex > 0) {
-            ShortcutKeySeparator()
-        }
-        if (key is ShortcutKey.Text) {
-            ShortcutTextKey(key)
-        } else if (key is ShortcutKey.Icon) {
-            ShortcutIconKey(key)
+private fun PressedKeysTextContainer(pressedKeys: List<ShortcutKey>) {
+    Row(
+        modifier =
+            Modifier.semantics(mergeDescendants = true) { liveRegion = LiveRegionMode.Polite },
+        verticalAlignment = Alignment.CenterVertically,
+    ) {
+        pressedKeys.forEachIndexed { keyIndex, key ->
+            if (keyIndex > 0) {
+                ShortcutKeySeparator()
+            }
+            if (key is ShortcutKey.Text) {
+                ShortcutTextKey(key)
+            } else if (key is ShortcutKey.Icon) {
+                ShortcutIconKey(key)
+            }
         }
     }
 }
@@ -338,7 +360,7 @@
 }
 
 @Composable
-private fun RowScope.ShortcutIconKey(key: ShortcutKey.Icon) {
+private fun ShortcutIconKey(key: ShortcutKey.Icon) {
     Icon(
         painter =
             when (key) {
@@ -346,7 +368,7 @@
                 is ShortcutKey.Icon.DrawableIcon -> rememberDrawablePainter(drawable = key.drawable)
             },
         contentDescription = null,
-        modifier = Modifier.align(Alignment.CenterVertically).height(24.dp),
+        modifier = Modifier.height(24.dp),
         tint = MaterialTheme.colorScheme.onSurfaceVariant,
     )
 }
@@ -397,6 +419,7 @@
                 .width(316.dp)
                 .wrapContentSize(Alignment.Center),
         color = MaterialTheme.colorScheme.onSurfaceVariant,
+        textAlign = TextAlign.Center,
     )
 }
 
@@ -464,3 +487,31 @@
         modifier = Modifier.padding(vertical = 12.dp).size(24.dp).wrapContentSize(Alignment.Center),
     )
 }
+
+@Composable
+private fun OutlinedInputField(
+    content: @Composable (() -> Unit)?,
+    placeholder: @Composable () -> Unit,
+    trailingIcon: @Composable () -> Unit,
+    isError: Boolean,
+    modifier: Modifier = Modifier,
+) {
+    OutlinedTextField(
+        value = "",
+        onValueChange = {},
+        placeholder = if (content == null) placeholder else null,
+        prefix = content,
+        singleLine = true,
+        modifier = modifier,
+        trailingIcon = trailingIcon,
+        colors =
+            OutlinedTextFieldDefaults.colors()
+                .copy(
+                    focusedIndicatorColor = MaterialTheme.colorScheme.primary,
+                    unfocusedIndicatorColor = MaterialTheme.colorScheme.outline,
+                    errorIndicatorColor = MaterialTheme.colorScheme.error,
+                ),
+        shape = RoundedCornerShape(50.dp),
+        isError = isError,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 0054dd7..6395bb73 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
Binary files differ
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
index 915a66c..f4ba99c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
@@ -28,16 +28,17 @@
 import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutCustomizationInteractor
 import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.AddShortcutDialog
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.DeleteShortcutDialog
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.ResetShortcutDialog
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.res.R
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.update
 
 class ShortcutCustomizationViewModel
@@ -45,26 +46,12 @@
 constructor(
     private val context: Context,
     private val shortcutCustomizationInteractor: ShortcutCustomizationInteractor,
-) {
+) : ExclusiveActivatable() {
     private var keyDownEventCache: KeyEvent? = null
     private val _shortcutCustomizationUiState =
         MutableStateFlow<ShortcutCustomizationUiState>(ShortcutCustomizationUiState.Inactive)
 
-    val shortcutCustomizationUiState =
-        shortcutCustomizationInteractor.pressedKeys
-            .map { keys ->
-                // Note that Action Key is excluded as it's already displayed on the UI
-                keys.filter {
-                    it != shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey()
-                }
-            }
-            .combine(_shortcutCustomizationUiState) { keys, uiState ->
-                if (uiState is AddShortcutDialog) {
-                    uiState.copy(pressedKeys = keys)
-                } else {
-                    uiState
-                }
-            }
+    val shortcutCustomizationUiState = _shortcutCustomizationUiState.asStateFlow()
 
     fun onShortcutCustomizationRequested(requestInfo: ShortcutCustomizationRequestInfo) {
         shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
@@ -92,7 +79,7 @@
     fun onDialogDismissed() {
         _shortcutCustomizationUiState.value = ShortcutCustomizationUiState.Inactive
         shortcutCustomizationInteractor.onCustomizationRequested(null)
-        shortcutCustomizationInteractor.updateUserSelectedKeyCombination(null)
+        clearSelectedKeyCombination()
     }
 
     fun onShortcutKeyCombinationSelected(keyEvent: KeyEvent): Boolean {
@@ -112,7 +99,6 @@
 
     suspend fun onSetShortcut() {
         val result = shortcutCustomizationInteractor.confirmAndSetShortcutCurrentlyBeingCustomized()
-
         _shortcutCustomizationUiState.update { uiState ->
             when (result) {
                 ShortcutCustomizationRequestResult.SUCCESS -> ShortcutCustomizationUiState.Inactive
@@ -158,6 +144,10 @@
         }
     }
 
+    fun clearSelectedKeyCombination() {
+        shortcutCustomizationInteractor.updateUserSelectedKeyCombination(null)
+    }
+
     private fun getUiStateWithErrorMessage(
         uiState: ShortcutCustomizationUiState,
         errorMessage: String,
@@ -180,11 +170,41 @@
         keyDownEventCache = null
     }
 
+    private suspend fun isSelectedKeyCombinationAvailable() =
+        shortcutCustomizationInteractor.isSelectedKeyCombinationAvailable()
+
     @AssistedFactory
     interface Factory {
         fun create(): ShortcutCustomizationViewModel
     }
 
+    override suspend fun onActivated(): Nothing {
+        shortcutCustomizationInteractor.pressedKeys.collect {
+            val keys = filterDefaultCustomShortcutModifierKey(it)
+            val errorMessage = getErrorMessageForPressedKeys(keys)
+
+            _shortcutCustomizationUiState.update { uiState ->
+                if (uiState is AddShortcutDialog) {
+                    uiState.copy(pressedKeys = keys, errorMessage = errorMessage)
+                } else {
+                    uiState
+                }
+            }
+        }
+    }
+
+    private suspend fun getErrorMessageForPressedKeys(keys: List<ShortcutKey>): String {
+        return if (keys.isEmpty() or isSelectedKeyCombinationAvailable()) {
+            ""
+        }
+        else {
+            context.getString(R.string.shortcut_customizer_key_combination_in_use_error_message)
+        }
+    }
+
+    private fun filterDefaultCustomShortcutModifierKey(keys: List<ShortcutKey>) =
+        keys.filter { it != shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey() }
+
     companion object {
         private val SUPPORTED_MODIFIERS =
             listOf(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModel.kt
index 26eb706..d0d1474 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModel.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.keyboard.stickykeys.shared.model.Locked
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.flatMapLatest
@@ -38,7 +37,6 @@
     @Application applicationScope: CoroutineScope,
 ) {
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     val indicatorContent: Flow<Map<ModifierKey, Locked>> =
         keyboardRepository.isAnyKeyboardConnected
             .flatMapLatest { keyboardPresent ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 5e0768a..df3633be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor
 import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
 import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder
@@ -38,6 +39,8 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.dagger.KeyguardBlueprintLog
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.ShadeDisplayAware
@@ -54,10 +57,8 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Binds keyguard views on startup, and also exposes methods to allow rebinding if views change */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class KeyguardViewConfigurator
 @Inject
@@ -79,6 +80,7 @@
     private val keyguardClockViewModel: KeyguardClockViewModel,
     private val smartspaceViewModel: KeyguardSmartspaceViewModel,
     private val clockInteractor: KeyguardClockInteractor,
+    private val wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor,
     private val keyguardViewMediator: KeyguardViewMediator,
     private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>,
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
@@ -87,6 +89,7 @@
     private val wallpaperViewModel: WallpaperViewModel,
     @Main private val mainDispatcher: CoroutineDispatcher,
     private val msdlPlayer: MSDLPlayer,
+    @KeyguardBlueprintLog private val blueprintLog: LogBuffer,
 ) : CoreStartable {
 
     private var rootViewHandle: DisposableHandle? = null
@@ -109,6 +112,7 @@
                 keyguardBlueprintViewModel,
                 keyguardClockViewModel,
                 smartspaceViewModel,
+                blueprintLog,
             )
         }
         if (deviceEntryUnlockTrackerViewBinder.isPresent) {
@@ -139,6 +143,7 @@
                 screenOffAnimationController,
                 shadeInteractor,
                 clockInteractor,
+                wallpaperFocalAreaInteractor,
                 keyguardClockViewModel,
                 interactionJankMonitor,
                 deviceEntryHapticsInteractor,
@@ -148,6 +153,7 @@
                 statusBarKeyguardViewManager,
                 mainDispatcher,
                 msdlPlayer,
+                blueprintLog,
             )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/GlanceableHubBlurComponent.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/GlanceableHubBlurComponent.kt
new file mode 100644
index 0000000..c0411b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/GlanceableHubBlurComponent.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 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.keyguard.dagger
+
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubBlurProvider
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+@Subcomponent
+interface GlanceableHubBlurComponent {
+    @Subcomponent.Factory
+    interface Factory {
+        fun create(
+            @BindsInstance animation: KeyguardTransitionAnimationFlow.FlowBuilder
+        ): GlanceableHubBlurComponent
+    }
+
+    fun getBlurProvider(): GlanceableHubBlurProvider
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/GlanceableHubTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/GlanceableHubTransitionModule.kt
new file mode 100644
index 0000000..affdee8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/GlanceableHubTransitionModule.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2025 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.keyguard.dagger
+
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
+import com.android.systemui.keyguard.ui.viewmodel.DozingToGlanceableHubTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDozingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToGlanceableHubTransitionViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import dagger.multibindings.Multibinds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@Module(subcomponents = [GlanceableHubBlurComponent::class])
+interface GlanceableHubTransitionModule {
+    @Multibinds fun glanceableHubTransition(): Set<GlanceableHubTransition>
+}
+
+@ExperimentalCoroutinesApi
+@Module
+interface GlanceableHubTransitionImplModule {
+    @Binds
+    @IntoSet
+    fun fromLockscreen(impl: LockscreenToGlanceableHubTransitionViewModel): GlanceableHubTransition
+
+    @Binds
+    @IntoSet
+    fun toLockScreen(impl: GlanceableHubToLockscreenTransitionViewModel): GlanceableHubTransition
+
+    @Binds
+    @IntoSet
+    fun fromOccluded(impl: OccludedToGlanceableHubTransitionViewModel): GlanceableHubTransition
+
+    @Binds
+    @IntoSet
+    fun toOccluded(impl: GlanceableHubToOccludedTransitionViewModel): GlanceableHubTransition
+
+    @Binds
+    @IntoSet
+    fun fromDream(impl: DreamingToGlanceableHubTransitionViewModel): GlanceableHubTransition
+
+    @Binds
+    @IntoSet
+    fun toDream(impl: GlanceableHubToDreamingTransitionViewModel): GlanceableHubTransition
+
+    @Binds
+    @IntoSet
+    fun fromDozing(impl: DozingToGlanceableHubTransitionViewModel): GlanceableHubTransition
+
+    @Binds
+    @IntoSet
+    fun toDozing(impl: GlanceableHubToDozingTransitionViewModel): GlanceableHubTransition
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index a7a43249..905bbe2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -19,6 +19,7 @@
 import android.app.IActivityTaskManager;
 import android.app.trust.TrustManager;
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.PowerManager;
 
 import com.android.internal.jank.InteractionJankMonitor;
@@ -34,6 +35,7 @@
 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
 import com.android.keyguard.mediator.ScreenOnCoordinator;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.bouncer.dagger.BouncerLoggerModule;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -59,12 +61,14 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionBootInteractor;
 import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
+import com.android.systemui.keyguard.ui.transitions.BlurConfig;
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
 import com.android.systemui.keyguard.ui.view.AlternateBouncerWindowViewBinder;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModelModule;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.process.ProcessWrapper;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeDisplayAware;
@@ -94,20 +98,20 @@
 import dagger.multibindings.IntoMap;
 
 import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
 
 import java.util.concurrent.Executor;
 
 /**
  * Dagger Module providing keyguard.
  */
-@ExperimentalCoroutinesApi
 @Module(subcomponents = {
         KeyguardQsUserSwitchComponent.class,
         KeyguardStatusBarViewComponent.class},
         includes = {
             DeviceEntryIconTransitionModule.class,
             FalsingModule.class,
+            GlanceableHubTransitionModule.class,
+            GlanceableHubTransitionImplModule.class,
             PrimaryBouncerTransitionModule.class,
             PrimaryBouncerTransitionImplModule.class,
             KeyguardDataQuickAffordanceModule.class,
@@ -238,6 +242,20 @@
     /** */
     @Provides
     @SysUISingleton
+    static BlurConfig provideBlurConfig(@Main Resources resources) {
+        int minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius);
+        int maxBlurRadius =
+                Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()
+                        || Flags.glanceableHubBlurredBackground()
+                        ? resources.getDimensionPixelSize(R.dimen.max_shade_window_blur_radius)
+                        : resources.getDimensionPixelSize(R.dimen.max_window_blur_radius);
+
+        return new BlurConfig(minBlurRadius, maxBlurRadius);
+    }
+
+    /** */
+    @Provides
+    @SysUISingleton
     static ThreadAssert providesThreadAssert() {
         return new ThreadAssert();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt
index d3e2560..7c4dbfe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt
@@ -16,11 +16,6 @@
 
 package com.android.systemui.keyguard.dagger
 
-import android.content.res.Resources
-import com.android.systemui.Flags
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.ui.transitions.BlurConfig
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodToPrimaryBouncerTransitionViewModel
@@ -34,13 +29,10 @@
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToOccludedTransitionViewModel
-import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
-import dagger.Provides
 import dagger.multibindings.IntoSet
 import dagger.multibindings.Multibinds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /**
  * Base module that defines the [PrimaryBouncerTransition] multibinding. All variants of SystemUI
@@ -49,28 +41,12 @@
 @Module
 interface PrimaryBouncerTransitionModule {
     @Multibinds fun primaryBouncerTransitions(): Set<PrimaryBouncerTransition>
-
-    companion object {
-        @Provides
-        @SysUISingleton
-        fun provideBlurConfig(@Main resources: Resources): BlurConfig {
-            val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
-            val maxBlurRadius =
-                if (Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) {
-                    resources.getDimensionPixelSize(R.dimen.max_shade_window_blur_radius)
-                } else {
-                    resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
-                }
-            return BlurConfig(minBlurRadius.toFloat(), maxBlurRadius.toFloat())
-        }
-    }
 }
 
 /**
  * Module that installs all the implementations of [PrimaryBouncerTransition] from different
  * keyguard states to and away from the primary bouncer.
  */
-@ExperimentalCoroutinesApi
 @Module
 interface PrimaryBouncerTransitionImplModule {
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
index c774987..3968b49 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -43,7 +42,6 @@
  * the question "which affordances should the keyguard show?" for the user associated with the
  * System UI process.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardQuickAffordanceLocalUserSelectionManager
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
index 8be11a4..ea3662b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -44,7 +43,6 @@
  * the question "which affordances should the keyguard show?" for users associated with other System
  * UI processes.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardQuickAffordanceRemoteUserSelectionManager
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index cb7702e..760adbf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -38,7 +38,6 @@
 import com.android.systemui.wallet.util.getPaymentCards
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -63,7 +62,6 @@
 
     override val pickerIconResourceId = R.drawable.ic_wallet_lockscreen
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
         conflatedCallbackFlow {
                 val callback =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 4e7de5d..dd2bec1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -50,7 +50,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -122,7 +121,6 @@
 
 private const val TAG = "BiometricsRepositoryImpl"
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class BiometricSettingsRepositoryImpl
 @Inject
@@ -387,7 +385,6 @@
             .and(isFaceAuthSupportedInCurrentPosture)
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
 private class StrongAuthTracker(
     private val userRepository: UserRepository,
     @ShadeDisplayAware context: Context?
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
index b826a00..37b657f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
@@ -18,7 +18,6 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.os.Handler
-import android.util.Log
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
@@ -26,6 +25,9 @@
 import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
 import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.KeyguardBlueprintLog
 import com.android.systemui.util.ThreadAssert
 import java.io.PrintWriter
 import java.util.TreeMap
@@ -51,7 +53,10 @@
     blueprints: Set<@JvmSuppressWildcards KeyguardBlueprint>,
     @Main val handler: Handler,
     val assert: ThreadAssert,
+    @KeyguardBlueprintLog log: LogBuffer,
 ) {
+    private val logger = Logger(log, "KeyguardBlueprintRepository")
+
     // This is TreeMap so that we can order the blueprints and assign numerical values to the
     // blueprints in the adb tool.
     private val blueprintIdMap: TreeMap<String, KeyguardBlueprint> =
@@ -69,11 +74,12 @@
     fun applyBlueprint(blueprintId: String?): Boolean {
         val blueprint = blueprintIdMap[blueprintId]
         if (blueprint == null) {
-            Log.e(
-                TAG,
-                "Could not find blueprint with id: $blueprintId. " +
+            logger.e({
+                "Could not find blueprint with id: $str1. " +
                     "Perhaps it was not added to KeyguardBlueprintModule?"
-            )
+            }) {
+                str1 = blueprintId
+            }
             return false
         }
 
@@ -99,7 +105,9 @@
                 targetTransitionConfig?.let {
                     val success = refreshTransition.tryEmit(it)
                     if (!success) {
-                        Log.e(TAG, "refreshBlueprint: Failed to emit blueprint refresh: $it")
+                        logger.e({ "refreshBlueprint: Failed to emit blueprint refresh: $str1" }) {
+                            str1 = "$it"
+                        }
                     }
                 }
                 targetTransitionConfig = null
@@ -110,6 +118,8 @@
         if ((targetTransitionConfig?.type?.priority ?: Int.MIN_VALUE) < config.type.priority) {
             if (targetTransitionConfig == null) scheduleCallback()
             targetTransitionConfig = config
+        } else {
+            logger.i({ "Skipping low priority transition: $str1" }) { str1 = "$config" }
         }
     }
 
@@ -117,8 +127,4 @@
     fun printBlueprints(pw: PrintWriter) {
         blueprintIdMap.onEachIndexed { index, entry -> pw.println("$index: ${entry.key}") }
     }
-
-    companion object {
-        private const val TAG = "KeyguardBlueprintRepository"
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index c1ec88b..aa28c5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -41,7 +41,6 @@
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,7 +52,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** Abstracts access to application state related to keyguard quick affordances. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardQuickAffordanceRepository
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index a39982d..621cc46 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.graphics.Point
+import android.graphics.RectF
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.widget.LockPatternUtils
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -49,7 +50,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -258,6 +258,8 @@
 
     val notificationStackAbsoluteBottom: StateFlow<Float>
 
+    val wallpaperFocalAreaBounds: StateFlow<RectF>
+
     /**
      * Returns `true` if the keyguard is showing; `false` otherwise.
      *
@@ -329,6 +331,8 @@
      * this value
      */
     fun setNotificationStackAbsoluteBottom(bottom: Float)
+
+    fun setWallpaperFocalAreaBounds(bounds: RectF)
 }
 
 /** Encapsulates application state for the keyguard. */
@@ -380,7 +384,6 @@
     override val onCameraLaunchDetected = MutableStateFlow(CameraLaunchSourceModel())
 
     override val panelAlpha: MutableStateFlow<Float> = MutableStateFlow(1f)
-
     override val topClippingBounds = MutableStateFlow<Int?>(null)
 
     override val isKeyguardShowing: MutableStateFlow<Boolean> =
@@ -531,7 +534,6 @@
         awaitClose { dozeTransitionListener.removeCallback(callback) }
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     override val isEncryptedOrLockdown: Flow<Boolean> =
         conflatedCallbackFlow {
                 val callback =
@@ -622,6 +624,10 @@
     private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
     override val notificationStackAbsoluteBottom = _notificationStackAbsoluteBottom.asStateFlow()
 
+    private val _wallpaperFocalAreaBounds = MutableStateFlow(RectF(0F, 0F, 0F, 0F))
+    override val wallpaperFocalAreaBounds: StateFlow<RectF> =
+        _wallpaperFocalAreaBounds.asStateFlow()
+
     init {
         val callback =
             object : KeyguardStateController.Callback {
@@ -700,6 +706,10 @@
         _notificationStackAbsoluteBottom.value = bottom
     }
 
+    override fun setWallpaperFocalAreaBounds(bounds: RectF) {
+        _wallpaperFocalAreaBounds.value = bounds
+    }
+
     private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel {
         return when (state) {
             DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
index 4f6319a..0871f13 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.keyguard.data.repository
 
 import android.content.Context
@@ -37,7 +35,6 @@
 import com.android.systemui.statusbar.PowerButtonReveal
 import javax.inject.Inject
 import kotlin.math.max
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
index bd5d096..c5a6fa1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
@@ -31,7 +31,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -71,7 +70,6 @@
     suspend fun reportKeyguardShowingChanged()
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class TrustRepositoryImpl
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt
index ebc3483..056d365 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt
@@ -15,10 +15,8 @@
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
 import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.StateFlow
 
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class BiometricUnlockInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index 68ec4f3..ccff49a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -40,7 +39,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates business-logic related to Ambient Display burn-in offsets. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class BurnInInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DevicePostureInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DevicePostureInteractor.kt
index e48cddb..8af1736 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DevicePostureInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DevicePostureInteractor.kt
@@ -19,10 +19,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.DevicePostureRepository
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** DevicePosture business logic. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DevicePostureInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 7174379..4ad04be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -193,7 +193,10 @@
         if (SceneContainerFlag.isEnabled) return
         scope.launch("$TAG#listenForAodToPrimaryBouncer") {
             keyguardInteractor.primaryBouncerShowing
-                .filterRelevantKeyguardStateAnd { primaryBouncerShowing -> primaryBouncerShowing }
+                .filterRelevantKeyguardStateAnd { primaryBouncerShowing ->
+                    !isWakeAndUnlock(keyguardInteractor.biometricUnlockState.value.mode) &&
+                        primaryBouncerShowing
+                }
                 .collect { startTransitionTo(KeyguardState.PRIMARY_BOUNCER) }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 82b8ca2..251af11 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -44,12 +44,10 @@
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.debounce
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class FromDreamingTransitionInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 3565b61..c5d40a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -48,7 +48,6 @@
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.debounce
-import com.android.app.tracing.coroutines.launchTraced as launch
 import kotlinx.coroutines.withContext
 
 @OptIn(FlowPreview::class)
@@ -92,6 +91,7 @@
         listenForHubToAlternateBouncer()
         listenForHubToOccluded()
         listenForHubToGone()
+        listenForHubToDreaming()
     }
 
     override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
@@ -177,6 +177,24 @@
         }
     }
 
+    private fun listenForHubToDreaming() {
+        if (!communalSettingsInteractor.isV2FlagEnabled()) {
+            return
+        }
+
+        scope.launch {
+            keyguardInteractor.isAbleToDream
+                .filterRelevantKeyguardStateAnd { isAbleToDream -> isAbleToDream }
+                .collect {
+                    communalSceneInteractor.changeScene(
+                        newScene = CommunalScenes.Blank,
+                        loggingReason = "hub to dreaming",
+                        keyguardState = KeyguardState.DREAMING,
+                    )
+                }
+        }
+    }
+
     private fun listenForHubToOccluded() {
         if (KeyguardWmStateRefactor.isEnabled) {
             scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 8c60371..cf712f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -19,8 +19,12 @@
 import android.animation.ValueAnimator
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Flags.communalSceneKtfRefactor
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -39,6 +43,10 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.util.kotlin.sample
+import java.util.UUID
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -47,11 +55,6 @@
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
-import java.util.UUID
-import javax.inject.Inject
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 @SysUISingleton
 class FromLockscreenTransitionInteractor
@@ -68,6 +71,8 @@
     powerInteractor: PowerInteractor,
     private val glanceableHubTransitions: GlanceableHubTransitions,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
+    private val communalInteractor: CommunalInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     private val swipeToDismissInteractor: SwipeToDismissInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
 ) :
@@ -94,6 +99,9 @@
         if (!communalSceneKtfRefactor()) {
             listenForLockscreenToGlanceableHub()
         }
+        if (communalSettingsInteractor.isV2FlagEnabled()) {
+            listenForLockscreenToGlanceableHubV2()
+        }
     }
 
     /**
@@ -268,9 +276,7 @@
                     it.transitionState == TransitionState.CANCELED &&
                         it.to == KeyguardState.PRIMARY_BOUNCER
                 }
-                .collect {
-                    transitionId = null
-                }
+                .collect { transitionId = null }
         }
     }
 
@@ -370,6 +376,19 @@
         }
     }
 
+    private fun listenForLockscreenToGlanceableHubV2() {
+        scope.launch {
+            communalInteractor.shouldShowCommunal
+                .filterRelevantKeyguardStateAnd { shouldShow -> shouldShow }
+                .collect {
+                    communalSceneInteractor.changeScene(
+                        newScene = CommunalScenes.Communal,
+                        loggingReason = "lockscreen to communal",
+                    )
+                }
+        }
+    }
+
     override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
         return ValueAnimator().apply {
             interpolator = Interpolators.LINEAR
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 2aaec87..fc79c7f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -15,8 +15,6 @@
  *
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.app.tracing.coroutines.launchTraced as launch
@@ -37,7 +35,6 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
index cc8652c..d8c707f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
@@ -36,7 +36,6 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -52,7 +51,6 @@
 import kotlinx.coroutines.launch
 
 /** Encapsulates business-logic for actions to run when the keyguard is dismissed. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class KeyguardDismissActionInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 42cbd7d..a1f288e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -24,6 +24,8 @@
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.kotlin.sample
@@ -32,6 +34,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
@@ -166,4 +169,14 @@
             isKeyguardEnabled.value && lockPatternUtils.isLockScreenDisabled(userId)
         }
     }
+
+    suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+        isKeyguardEnabled
+            .logDiffsForTable(
+                tableLogBuffer = tableLogBuffer,
+                columnName = "isKeyguardEnabled",
+                initialValue = isKeyguardEnabled.value,
+            )
+            .collect()
+    }
 }
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 1f3c08c..3739d17 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
@@ -13,8 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.keyguard.domain.interactor
 
 import android.app.StatusBarManager
@@ -44,6 +42,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -55,7 +55,6 @@
 import javax.inject.Inject
 import javax.inject.Provider
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
@@ -63,6 +62,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.combineTransform
 import kotlinx.coroutines.flow.debounce
@@ -536,6 +536,16 @@
         repository.setNotificationStackAbsoluteBottom(bottom)
     }
 
+    suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+        isDozing
+            .logDiffsForTable(
+                tableLogBuffer = tableLogBuffer,
+                columnName = "isDozing",
+                initialValue = isDozing.value,
+            )
+            .collect()
+    }
+
     companion object {
         private const val TAG = "KeyguardInteractor"
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index 2d81be6..6d9b276 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -32,10 +32,8 @@
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Handles key events arriving when the keyguard is showing or device is dozing. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class KeyguardKeyEventInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index b866fca..898b68d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -61,7 +61,6 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -73,7 +72,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardQuickAffordanceInteractor
 @Inject
@@ -477,7 +475,7 @@
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
                 value =
-                    com.android.systemui.Flags.lockscreenCustomClocks() ||
+                    com.android.systemui.shared.Flags.lockscreenCustomClocks() ||
                         featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
             ),
             KeyguardPickerFlag(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
index a650300..705eaa2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
@@ -38,7 +38,6 @@
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,7 +52,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** Business logic for use-cases related to top-level touch handling in the lock screen. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardTouchHandlingInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
index b986d56..2154564 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
@@ -51,7 +50,6 @@
      * Whether the lockscreen should be showing when the device starts up for the first time. If not
      * then we'll seed the repository with a transition from OFF -> GONE.
      */
-    @OptIn(ExperimentalCoroutinesApi::class)
     private val showLockscreenOnBoot: Flow<Boolean> by lazy {
         deviceProvisioningInteractor.isDeviceProvisioned.map { provisioned ->
             (provisioned || deviceEntryInteractor.isAuthenticationRequired()) &&
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 f078fe2..58fb423 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
@@ -41,7 +41,6 @@
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancelAndJoin
 import kotlinx.coroutines.channels.BufferOverflow
@@ -65,7 +64,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates business-logic related to the keyguard transitions. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardTransitionInteractor
 @Inject
@@ -127,7 +125,7 @@
         repository.transitions
             .pairwise()
             .filter { it.newValue.transitionState == TransitionState.STARTED }
-            .shareIn(scope, SharingStarted.Eagerly)
+            .shareIn(scope, SharingStarted.Eagerly, replay = 1)
 
     init {
         // Collect non-canceled steps and emit transition values.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 6d9c6a66..0b116de 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -13,13 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.keyguard.logging.ScrimLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_DURATION
 import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
 import com.android.systemui.keyguard.shared.model.Edge
@@ -33,13 +33,13 @@
 import com.android.systemui.util.kotlin.sample
 import dagger.Lazy
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.flowOn
 
 @SysUISingleton
 class LightRevealScrimInteractor
@@ -50,6 +50,7 @@
     @Application private val scope: CoroutineScope,
     private val scrimLogger: ScrimLogger,
     private val powerInteractor: Lazy<PowerInteractor>,
+    @Background backgroundDispatcher: CoroutineDispatcher,
 ) {
     init {
         listenForStartedKeyguardTransitionStep()
@@ -116,6 +117,7 @@
                     repository.maxAlpha
                 }
             }
+            .flowOn(backgroundDispatcher)
 
     val revealAmount =
         repository.revealAmount.filter {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
index 63bfba6..4d86677 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
@@ -26,14 +26,12 @@
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.statusbar.phone.hideAffordancesRequest
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 
 /** Encapsulates business logic for transitions between UDFPS states on the keyguard. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class UdfpsKeyguardInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt
new file mode 100644
index 0000000..934afe2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt
@@ -0,0 +1,176 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.RectF
+import android.util.TypedValue
+import com.android.app.animation.MathUtils.max
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository
+import javax.inject.Inject
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class WallpaperFocalAreaInteractor
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    context: Context,
+    private val keyguardRepository: KeyguardRepository,
+    shadeRepository: ShadeRepository,
+    activeNotificationsInteractor: ActiveNotificationsInteractor,
+    keyguardClockRepository: KeyguardClockRepository,
+    wallpaperRepository: WallpaperRepository,
+) {
+    // When there's notifications in splitshade, magic portrait shape effects should be left
+    // aligned in foldable
+    private val notificationInShadeWideLayout: Flow<Boolean> =
+        combine(
+            shadeRepository.isShadeLayoutWide,
+            activeNotificationsInteractor.areAnyNotificationsPresent,
+        ) { isShadeLayoutWide, areAnyNotificationsPresent: Boolean ->
+            when {
+                !isShadeLayoutWide -> false
+                !areAnyNotificationsPresent -> false
+                else -> true
+            }
+        }
+
+    val shouldSendFocalArea = wallpaperRepository.shouldSendFocalArea
+    val wallpaperFocalAreaBounds: StateFlow<RectF?> =
+        combine(
+                shadeRepository.isShadeLayoutWide,
+                notificationInShadeWideLayout,
+                keyguardRepository.notificationStackAbsoluteBottom,
+                keyguardRepository.shortcutAbsoluteTop,
+                keyguardClockRepository.notificationDefaultTop,
+            ) {
+                isShadeLayoutWide,
+                notificationInShadeWideLayout,
+                notificationStackAbsoluteBottom,
+                shortcutAbsoluteTop,
+                notificationDefaultTop ->
+                // Wallpaper will be zoomed in with config_wallpaperMaxScale in lockscreen
+                // so we need to give a bounds taking this scale in consideration
+                val wallpaperZoomedInScale = getSystemWallpaperMaximumScale(context)
+                val screenBounds =
+                    RectF(
+                        0F,
+                        0F,
+                        context.resources.displayMetrics.widthPixels.toFloat(),
+                        context.resources.displayMetrics.heightPixels.toFloat(),
+                    )
+                val scaledBounds =
+                    RectF(
+                        screenBounds.centerX() - screenBounds.width() / 2F / wallpaperZoomedInScale,
+                        screenBounds.centerY() -
+                            screenBounds.height() / 2F / wallpaperZoomedInScale,
+                        screenBounds.centerX() + screenBounds.width() / 2F / wallpaperZoomedInScale,
+                        screenBounds.centerY() + screenBounds.height() / 2F / wallpaperZoomedInScale,
+                    )
+                val maxFocalAreaWidth =
+                    TypedValue.applyDimension(
+                        TypedValue.COMPLEX_UNIT_DIP,
+                        FOCAL_AREA_MAX_WIDTH_DP.toFloat(),
+                        context.resources.displayMetrics,
+                    )
+                val (left, right) =
+                // tablet landscape
+                if (context.resources.getBoolean(R.bool.center_align_magic_portrait_shape)) {
+                        Pair(
+                            scaledBounds.centerX() - maxFocalAreaWidth / 2F,
+                            scaledBounds.centerX() + maxFocalAreaWidth / 2F,
+                        )
+                        // unfold foldable landscape
+                    } else if (isShadeLayoutWide) {
+                        if (notificationInShadeWideLayout) {
+                            Pair(scaledBounds.left, scaledBounds.centerX())
+                        } else {
+                            Pair(scaledBounds.centerX(), scaledBounds.right)
+                        }
+                        // handheld / portrait
+                    } else {
+                        val focalAreaWidth = min(scaledBounds.width(), maxFocalAreaWidth)
+                        Pair(
+                            scaledBounds.centerX() - focalAreaWidth / 2F,
+                            scaledBounds.centerX() + focalAreaWidth / 2F,
+                        )
+                    }
+                val scaledBottomMargin =
+                    (context.resources.displayMetrics.heightPixels - shortcutAbsoluteTop) /
+                        wallpaperZoomedInScale
+                val top =
+                    // tablet landscape
+                    if (context.resources.getBoolean(R.bool.center_align_magic_portrait_shape)) {
+                        // no strict constraints for top, use bottom margin to make it symmetric
+                        // vertically
+                        scaledBounds.top + scaledBottomMargin
+                    }
+                    // unfold foldable landscape
+                    else if (isShadeLayoutWide) {
+                        // For all landscape, we should use bottom of smartspace to constrain
+                        scaledBounds.top + notificationDefaultTop / wallpaperZoomedInScale
+                        // handheld / portrait
+                    } else {
+                        scaledBounds.top +
+                            max(notificationDefaultTop, notificationStackAbsoluteBottom) /
+                                wallpaperZoomedInScale
+                    }
+                val bottom = scaledBounds.bottom - scaledBottomMargin
+                RectF(left, top, right, bottom)
+            }
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = null,
+            )
+
+    fun setWallpaperFocalAreaBounds(bounds: RectF) {
+        keyguardRepository.setWallpaperFocalAreaBounds(bounds)
+    }
+
+    companion object {
+        fun getSystemWallpaperMaximumScale(context: Context): Float {
+            return context.resources.getFloat(
+                Resources.getSystem()
+                    .getIdentifier(
+                        /* name= */ "config_wallpaperMaxScale",
+                        /* defType= */ "dimen",
+                        /* defPackage= */ "android",
+                    )
+            )
+        }
+
+        // A max width for magic portrait shape effects bounds, to avoid it going too large
+        // in large screen portrait mode
+        const val FOCAL_AREA_MAX_WIDTH_DP = 500
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 184f302..61cf2cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.compose.animation.scene.ObservableTransitionState.Idle
@@ -24,7 +22,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.deviceIsAsleepInState
@@ -35,11 +32,9 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.toQuad
-import com.android.systemui.util.kotlin.sample
 import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import dagger.Lazy
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -47,7 +42,6 @@
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class WindowManagerLockscreenVisibilityInteractor
 @Inject
@@ -84,7 +78,6 @@
      * only be visible after swiping 20% of the way up the screen, and should become invisible again
      * if the user swipes back down.
      */
-    @OptIn(ExperimentalCoroutinesApi::class)
     private val transitionSpecificSurfaceBehindVisibility: Flow<Boolean?> =
         transitionInteractor.startedKeyguardTransitionStep
             .flatMapLatest { startedStep ->
@@ -122,7 +115,6 @@
      * transitioning between [KeyguardState]s or [Scenes] or the transition-specific visibility used
      * during certain ongoing transitions.
      */
-    @OptIn(ExperimentalCoroutinesApi::class)
     val surfaceBehindVisibility: Flow<Boolean> =
         if (SceneContainerFlag.isEnabled) {
                 sceneInteractor.get().transitionState.flatMapLatestConflated { state ->
@@ -239,12 +231,12 @@
     private val lockscreenVisibilityLegacy =
         combine(
                 transitionInteractor.currentKeyguardState,
+                transitionInteractor.startedStepWithPrecedingStep,
                 wakeToGoneInteractor.canWakeDirectlyToGone,
                 surfaceBehindVisibility,
-                ::Triple,
+                ::toQuad,
             )
-            .sample(transitionInteractor.startedStepWithPrecedingStep, ::toQuad)
-            .map { (currentState, canWakeDirectlyToGone, surfaceBehindVis, startedWithPrev) ->
+            .map { (currentState, startedWithPrev, canWakeDirectlyToGone, surfaceBehindVis) ->
                 val startedFromStep = startedWithPrev.previousValue
                 val startedStep = startedWithPrev.newValue
                 val returningToGoneAfterCancellation =
@@ -331,17 +323,9 @@
      * clock/smartspace/notif icons are visible.
      */
     val aodVisibility: Flow<Boolean> =
-        combine(
-                keyguardInteractor.isDozing,
-                keyguardInteractor.isAodAvailable,
-                keyguardInteractor.biometricUnlockState,
-            ) { isDozing, isAodAvailable, biometricUnlockState ->
-                // AOD is visible if we're dozing, unless we are wake and unlocking (where we go
-                // directly from AOD to unlocked while dozing).
-                isDozing &&
-                    isAodAvailable &&
-                    !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
-            }
+        transitionInteractor
+            .transitionValue(KeyguardState.AOD)
+            .map { it == 1f }
             .distinctUntilChanged()
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt
index 23b7b66..62e8474 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt
@@ -22,10 +22,8 @@
 import com.android.keyguard.AuthKeyguardMessageArea
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Binds the alternate bouncer message view to its view-model. */
-@ExperimentalCoroutinesApi
 object AlternateBouncerMessageAreaViewBinder {
 
     /** Binds the view to the view-model, continuing to update the former based on the latter. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
index 6ef9863..0b587ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
@@ -26,9 +26,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-@ExperimentalCoroutinesApi
 object AlternateBouncerUdfpsViewBinder {
 
     /** Updates UI for the UDFPS icon on the alternate bouncer. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 33783b5..fa64fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -47,7 +47,6 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /**
  * When necessary, adds the alternate bouncer window above most other windows (including the
@@ -56,7 +55,6 @@
  *
  * For devices that support UDFPS, this view includes a UDFPS view.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class AlternateBouncerViewBinder
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 3d6cf2f..e9a3bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -41,10 +41,8 @@
 import com.android.systemui.util.kotlin.DisposableHandles
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import com.android.app.tracing.coroutines.launchTraced as launch
 
-@ExperimentalCoroutinesApi
 object DeviceEntryIconViewBinder {
     private const val TAG = "DeviceEntryIconViewBinder"
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 261c130..5c4e34e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -17,7 +17,6 @@
 
 package com.android.systemui.keyguard.ui.binder
 
-import android.util.Log
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
@@ -25,13 +24,16 @@
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
-import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.BaseBlueprintTransition
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.KeyguardBlueprintLog
+import com.android.systemui.plugins.clocks.ClockLogger.Companion.getVisText
 import com.android.systemui.shared.R as sharedR
 import com.android.systemui.util.kotlin.pairwise
 
@@ -42,7 +44,9 @@
         viewModel: KeyguardBlueprintViewModel,
         clockViewModel: KeyguardClockViewModel,
         smartspaceViewModel: KeyguardSmartspaceViewModel,
+        @KeyguardBlueprintLog log: LogBuffer,
     ) {
+        val logger = Logger(log, TAG)
         constraintLayout.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
                 launch("$TAG#viewModel.blueprint") {
@@ -54,6 +58,7 @@
                                 config,
                                 clockViewModel,
                                 smartspaceViewModel,
+                                log,
                             )
 
                         viewModel.runTransition(constraintLayout, transition, config) {
@@ -74,7 +79,7 @@
                                     blueprint.applyConstraints(this)
                                 }
 
-                            logAlphaVisibilityScaleOfAppliedConstraintSet(cs, clockViewModel)
+                            logger.logConstraintSet(cs, clockViewModel)
                             cs.applyTo(constraintLayout)
                         }
                     }
@@ -97,7 +102,7 @@
                                     clone(constraintLayout)
                                     blueprint.applyConstraints(this)
                                 }
-                            logAlphaVisibilityScaleOfAppliedConstraintSet(cs, clockViewModel)
+                            logger.logConstraintSet(cs, clockViewModel)
                             cs.applyTo(constraintLayout)
                         }
                     }
@@ -106,35 +111,33 @@
         }
     }
 
-    private fun logAlphaVisibilityScaleOfAppliedConstraintSet(
-        cs: ConstraintSet,
-        viewModel: KeyguardClockViewModel,
-    ) {
+    private fun Logger.logConstraintSet(cs: ConstraintSet, viewModel: KeyguardClockViewModel) {
         val currentClock = viewModel.currentClock.value
-        if (!DEBUG || currentClock == null) return
-        val smallClockViewId = customR.id.lockscreen_clock_view
-        val largeClockViewId = currentClock.largeClock.layout.views[0].id
-        val smartspaceDateId = sharedR.id.date_smartspace_view
-        Log.i(
-            TAG,
-            "applyCsToSmallClock: vis=${cs.getVisibility(smallClockViewId)} " +
-                "alpha=${cs.getConstraint(smallClockViewId).propertySet.alpha} " +
-                "scale=${cs.getConstraint(smallClockViewId).transform.scaleX} ",
-        )
-        Log.i(
-            TAG,
-            "applyCsToLargeClock: vis=${cs.getVisibility(largeClockViewId)} " +
-                "alpha=${cs.getConstraint(largeClockViewId).propertySet.alpha} " +
-                "scale=${cs.getConstraint(largeClockViewId).transform.scaleX} " +
-                "pivotX=${cs.getConstraint(largeClockViewId).transform.transformPivotX} ",
-        )
-        Log.i(
-            TAG,
-            "applyCsToSmartspaceDate: vis=${cs.getVisibility(smartspaceDateId)} " +
-                "alpha=${cs.getConstraint(smartspaceDateId).propertySet.alpha}",
-        )
+        if (currentClock == null) return
+
+        this.i({ "applyCsToSmallClock: vis=${getVisText(int1)}; alpha=$str1; scale=$str2" }) {
+            val smallClockViewId = customR.id.lockscreen_clock_view
+            int1 = cs.getVisibility(smallClockViewId)
+            str1 = "${cs.getConstraint(smallClockViewId).propertySet.alpha}"
+            str2 = "${cs.getConstraint(smallClockViewId).transform.scaleX}"
+        }
+
+        this.i({
+            "applyCsToLargeClock: vis=${getVisText(int1)}; alpha=$str1; scale=$str2; pivotX=$str3"
+        }) {
+            val largeClockViewId = currentClock.largeClock.layout.views[0].id
+            int1 = cs.getVisibility(largeClockViewId)
+            str1 = "${cs.getConstraint(largeClockViewId).propertySet.alpha}"
+            str2 = "${cs.getConstraint(largeClockViewId).transform.scaleX}"
+            str3 = "${cs.getConstraint(largeClockViewId).transform.transformPivotX}"
+        }
+
+        this.i({ "applyCsToSmartspaceDate: vis=${getVisText(int1)}; alpha=$str1" }) {
+            val smartspaceDateId = sharedR.id.date_smartspace_view
+            int1 = cs.getVisibility(smartspaceDateId)
+            str1 = "${cs.getConstraint(smartspaceDateId).propertySet.alpha}"
+        }
     }
 
-    private const val TAG = "KeyguardBlueprintViewBinder"
-    private const val DEBUG = false
+    private val TAG = "KeyguardBlueprintViewBinder"
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
index f1a316c..7a08dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
@@ -23,11 +23,9 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Runs actions on keyguard dismissal. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardDismissActionBinder
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 92b49ed..1a8bf00 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -23,13 +23,13 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.KeyguardIndicationController
 import com.android.systemui.util.kotlin.DisposableHandles
 import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
@@ -42,7 +42,6 @@
  * view-binding, binding each view only once. It is okay and expected for the same instance of the
  * view-model to be reused for multiple view/view-binder bindings.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 object KeyguardIndicationAreaBinder {
 
     /** Binds the view to the view-model, continuing to update the former based on the latter. */
@@ -73,7 +72,6 @@
         disposables +=
             view.repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.STARTED) {
-
                     launch("$TAG#viewModel.indicationAreaTranslationX") {
                         viewModel.indicationAreaTranslationX.collect { translationX ->
                             view.translationX = translationX
@@ -119,6 +117,9 @@
                     launch("$TAG#viewModel.configurationChange") {
                         viewModel.configurationChange.collect {
                             configurationBasedDimensions.value = loadFromResources(view)
+                            if (Flags.indicationTextA11yFix()) {
+                                indicationController.onConfigurationChanged()
+                            }
                         }
                     }
 
@@ -140,7 +141,7 @@
                 view.resources.getDimensionPixelOffset(R.dimen.keyguard_indication_area_padding),
             indicationTextSizePx =
                 view.resources.getDimensionPixelSize(
-                    com.android.internal.R.dimen.text_size_small_material,
+                    com.android.internal.R.dimen.text_size_small_material
                 ),
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 6d270b2..017fe16 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -22,7 +22,6 @@
 import android.annotation.SuppressLint
 import android.graphics.Point
 import android.graphics.Rect
-import android.util.Log
 import android.view.HapticFeedbackConstants
 import android.view.InputDevice
 import android.view.MotionEvent
@@ -54,8 +53,10 @@
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.ui.view.layout.sections.AodPromotedNotificationSection
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
@@ -64,6 +65,9 @@
 import com.android.systemui.keyguard.ui.viewmodel.TransitionData
 import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.KeyguardBlueprintLog
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -85,13 +89,12 @@
 import kotlin.math.min
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.flow.update
 
 /** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
-@OptIn(ExperimentalCoroutinesApi::class)
 object KeyguardRootViewBinder {
     @SuppressLint("ClickableViewAccessibility")
     @JvmStatic
@@ -105,6 +108,7 @@
         screenOffAnimationController: ScreenOffAnimationController,
         shadeInteractor: ShadeInteractor,
         clockInteractor: KeyguardClockInteractor,
+        wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor,
         clockViewModel: KeyguardClockViewModel,
         interactionJankMonitor: InteractionJankMonitor?,
         deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor?,
@@ -114,6 +118,7 @@
         statusBarKeyguardViewManager: StatusBarKeyguardViewManager?,
         mainImmediateDispatcher: CoroutineDispatcher,
         msdlPlayer: MSDLPlayer?,
+        @KeyguardBlueprintLog blueprintLog: LogBuffer,
     ): DisposableHandle {
         val disposables = DisposableHandles()
         val childViews = mutableMapOf<Int, View>()
@@ -180,6 +185,7 @@
                         viewModel.translationY.collect { y ->
                             childViews[burnInLayerId]?.translationY = y
                             childViews[largeClockId]?.translationY = y
+                            childViews[aodPromotedNotificationId]?.translationY = y
                             childViews[aodNotificationIconContainerId]?.translationY = y
                         }
                     }
@@ -191,6 +197,7 @@
                                 state.isToOrFrom(KeyguardState.AOD) -> {
                                     // Large Clock is not translated in the x direction
                                     childViews[burnInLayerId]?.translationX = px
+                                    childViews[aodPromotedNotificationId]?.translationX = px
                                     childViews[aodNotificationIconContainerId]?.translationX = px
                                 }
                                 state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
@@ -287,11 +294,17 @@
                                 blueprintViewModel.refreshBlueprint()
                             }
                             childViews[aodNotificationIconContainerId]
-                                ?.setAodNotifIconContainerIsVisible(
-                                    isVisible,
-                                    iconsAppearTranslationPx.value,
-                                    screenOffAnimationController,
-                                )
+                                ?.setAodNotifIconContainerIsVisible(isVisible)
+                        }
+                    }
+
+                    launch {
+                        viewModel.isNotifIconContainerVisible.collect { isVisible ->
+                            if (isVisible.value) {
+                                blueprintViewModel.refreshBlueprint()
+                            }
+                            childViews[aodPromotedNotificationId]
+                                ?.setAodNotifIconContainerIsVisible(isVisible)
                         }
                     }
 
@@ -309,12 +322,15 @@
                                                 .setTag(clockId)
                                         jankMonitor.begin(builder)
                                     }
+
                                     TransitionState.CANCELED ->
                                         jankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
+
                                     TransitionState.FINISHED -> {
                                         keyguardViewMediator?.maybeHandlePendingLock()
                                         jankMonitor.end(CUJ_SCREEN_OFF_SHOW_AOD)
                                     }
+
                                     TransitionState.RUNNING -> Unit
                                 }
                             }
@@ -378,6 +394,21 @@
         }
 
         disposables +=
+            view.repeatWhenAttached {
+                repeatOnLifecycle(Lifecycle.State.STARTED) {
+                    if (viewModel.shouldSendFocalArea.value) {
+                        launch {
+                            wallpaperFocalAreaInteractor.wallpaperFocalAreaBounds
+                                .filterNotNull()
+                                .collect {
+                                    wallpaperFocalAreaInteractor.setWallpaperFocalAreaBounds(it)
+                                }
+                        }
+                    }
+                }
+            }
+
+        disposables +=
             view.onLayoutChanged(
                 OnLayoutChange(
                     viewModel,
@@ -385,6 +416,7 @@
                     clockViewModel,
                     childViews,
                     burnInParams,
+                    Logger(blueprintLog, TAG),
                 )
             )
 
@@ -442,6 +474,7 @@
         private val clockViewModel: KeyguardClockViewModel,
         private val childViews: Map<Int, View>,
         private val burnInParams: MutableStateFlow<BurnInParameters>,
+        private val logger: Logger,
     ) : OnLayoutChangeListener {
         var prevTransition: TransitionData? = null
 
@@ -462,7 +495,7 @@
                 val transition = blueprintViewModel.currentTransition.value
                 val shouldAnimate = transition != null && transition.config.type.animateNotifChanges
                 if (prevTransition == transition && shouldAnimate) {
-                    if (DEBUG) Log.w(TAG, "Skipping; layout during transition")
+                    logger.w("Skipping; layout during transition")
                     return
                 }
 
@@ -500,11 +533,7 @@
         }
     }
 
-    private fun View.setAodNotifIconContainerIsVisible(
-        isVisible: AnimatedValue<Boolean>,
-        iconsAppearTranslationPx: Int,
-        screenOffAnimationController: ScreenOffAnimationController,
-    ) {
+    private fun View.setAodNotifIconContainerIsVisible(isVisible: AnimatedValue<Boolean>) {
         animate().cancel()
         val animatorListener =
             object : AnimatorListenerAdapter() {
@@ -523,6 +552,7 @@
                         View.INVISIBLE
                     }
             }
+
             else -> {
                 if (isVisible.value) {
                     CrossFadeHelper.fadeIn(this, animatorListener)
@@ -538,6 +568,7 @@
     }
 
     private val burnInLayerId = R.id.burn_in_layer
+    private val aodPromotedNotificationId = AodPromotedNotificationSection.viewId
     private val aodNotificationIconContainerId = R.id.aod_notification_icon_container
     private val largeClockId = customR.id.lockscreen_clock_view_large
     private val smallClockId = customR.id.lockscreen_clock_view
@@ -551,5 +582,4 @@
     private const val ID = "occluding_app_device_entry_unlock_msg"
     private const val AOD_ICONS_APPEAR_DURATION: Long = 200
     private const val TAG = "KeyguardRootViewBinder"
-    private const val DEBUG = false
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index 13c2ffb..220846d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.keyguard.ui.binder
 
 import android.graphics.Rect
+import android.util.TypedValue
 import android.view.View
 import android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED
 import android.widget.TextView
@@ -101,6 +102,13 @@
                             }
                         }
                     }
+
+                    launch("$TAG#viewModel.textSize") {
+                        viewModel.textSize.collect { textSize ->
+                            val textView: TextView = view.requireViewById(R.id.text)
+                            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize.toFloat())
+                        }
+                    }
                 }
             }
         return disposableHandle
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index a210787..cff5ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -55,6 +55,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor
 import com.android.systemui.keyguard.shared.model.ClockSizeSetting
 import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
@@ -85,7 +86,6 @@
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
@@ -94,7 +94,6 @@
 
 /** Renders the preview of the lock screen. */
 class KeyguardPreviewRenderer
-@OptIn(ExperimentalCoroutinesApi::class)
 @AssistedInject
 constructor(
     @Application private val context: Context,
@@ -117,6 +116,7 @@
     private val secureSettings: SecureSettings,
     private val defaultShortcutsSection: DefaultShortcutsSection,
     private val keyguardQuickAffordanceViewBinder: KeyguardQuickAffordanceViewBinder,
+    private val wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor,
 ) {
     val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
     private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
@@ -321,7 +321,6 @@
         smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
         val keyguardRootView = KeyguardRootView(previewContext, null)
         rootView.addView(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt
index 3eb8522..542fb9b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt
@@ -23,10 +23,4 @@
     // No-op config that will be used by dagger of other SysUI variants which don't blur the
     // background surface.
     @Inject constructor() : this(0.0f, 0.0f)
-
-    companion object {
-        // Blur the shade much lesser than the background surface so that the surface is
-        // distinguishable from the background.
-        @JvmStatic fun Float.maxBlurRadiusToNotificationPanelBlurRadius(): Float = this / 3.0f
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index e2ad4635..dba2578 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -57,9 +57,7 @@
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-@ExperimentalCoroutinesApi
 @Module
 abstract class DeviceEntryIconTransitionModule {
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/GlanceableHubBlurProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/GlanceableHubBlurProvider.kt
new file mode 100644
index 0000000..19cd501
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/GlanceableHubBlurProvider.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.transitions
+
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * {@link GlanceableHubBlurProvider} helps provide a consistent blur experience across glanceable
+ * hub transitions by defining a single point where both the exit and entry flows are defined. Note
+ * that since these flows are driven by the specific transition animations, a singleton provider
+ * cannot be used.
+ */
+class GlanceableHubBlurProvider
+@Inject
+constructor(
+    transitionAnimation: KeyguardTransitionAnimationFlow.FlowBuilder,
+    blurConfig: BlurConfig,
+) {
+    val exitBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx)
+
+    val enterBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/GlanceableHubTransition.kt
similarity index 64%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
copy to packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/GlanceableHubTransition.kt
index aa262f9..1cc81f0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/GlanceableHubTransition.kt
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.utils
+package com.android.systemui.keyguard.ui.transitions
 
-import android.app.Instrumentation
+import kotlinx.coroutines.flow.Flow
 
-object RecentTasksUtils {
-    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
-        instrumentation.uiAutomation.executeShellCommand(
-            "dumpsys activity service SystemUIService WMShell recents clearAll"
-        )
-    }
-}
\ No newline at end of file
+interface GlanceableHubTransition {
+    /** Radius of blur applied to the window's root view. */
+    val windowBlurRadius: Flow<Float>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
index eb005f2..d280862 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
@@ -19,6 +19,7 @@
 package com.android.systemui.keyguard.ui.view
 
 import android.graphics.Rect
+import android.os.DeadObjectException
 import android.util.Log
 import android.view.View
 import com.android.systemui.dagger.SysUISingleton
@@ -76,9 +77,8 @@
     private var manualUnlockAmount: Float? = null
 
     /**
-     * Called from [OverviewProxyService] to provide us with the launcher unlock animation
-     * controller, which can be used to start and update the unlock animation in the launcher
-     * process.
+     * Called from Launcher to provide us with the launcher unlock animation controller, which can
+     * be used to start and update the unlock animation in the launcher process.
      */
     override fun setLauncherUnlockController(
         activityClass: String,
@@ -117,7 +117,7 @@
                 launcher.prepareForUnlock(
                     false,
                     Rect(),
-                    0
+                    0,
                 ) // TODO(b/293894758): Add smartspace animation support.
             }
         }
@@ -134,14 +134,14 @@
             Log.e(
                 TAG,
                 "Called prepareForUnlock(), but not playUnlockAnimation(). " +
-                    "Failing-safe by calling setUnlockAmount(1f)"
+                    "Failing-safe by calling setUnlockAmount(1f)",
             )
             setUnlockAmount(1f, forceIfAnimating = true)
         } else if (manualUnlockSetButNotFullyVisible) {
             Log.e(
                 TAG,
                 "Unlock has ended, but manual unlock amount != 1f. " +
-                    "Failing-safe by calling setUnlockAmount(1f)"
+                    "Failing-safe by calling setUnlockAmount(1f)",
             )
             setUnlockAmount(1f, forceIfAnimating = true)
         }
@@ -193,7 +193,12 @@
 
         launcherAnimationController?.let {
             manualUnlockAmount = amount
-            it.setUnlockAmount(amount, forceIfAnimating)
+
+            try {
+                it.setUnlockAmount(amount, forceIfAnimating)
+            } catch (e: DeadObjectException) {
+                Log.e(TAG, "DeadObjectException in setUnlockAmount($amount, $forceIfAnimating)", e)
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index 82abb05..af32339 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -39,14 +39,12 @@
 import javax.inject.Inject
 import javax.inject.Named
 import kotlin.jvm.optionals.getOrNull
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /**
  * Positions elements of the lockscreen to the default position.
  *
  * This will be the most common use case for phones in portrait mode.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 @JvmSuppressWildcards
 class DefaultKeyguardBlueprint
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
index 9a55f7b..0fb1af3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
@@ -22,11 +22,13 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.transitions.DefaultClockSteppingTransition
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.log.LogBuffer
 
 class IntraBlueprintTransition(
     config: IntraBlueprintTransition.Config,
     clockViewModel: KeyguardClockViewModel,
     smartspaceViewModel: KeyguardSmartspaceViewModel,
+    logBuffer: LogBuffer,
 ) : TransitionSet() {
 
     enum class Type(val priority: Int, val animateNotifChanges: Boolean) {
@@ -63,7 +65,7 @@
                 addTransition(
                     clockViewModel.currentClock.value?.let { DefaultClockSteppingTransition(it) }
                 )
-            else -> addTransition(ClockSizeTransition(config, clockViewModel))
+            else -> addTransition(ClockSizeTransition(config, clockViewModel, logBuffer))
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index e8fce9c..0c7e865 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -21,7 +21,6 @@
 import android.graphics.Point
 import android.graphics.Rect
 import android.util.DisplayMetrics
-import android.util.Log
 import android.view.WindowManager
 import androidx.annotation.VisibleForTesting
 import androidx.constraintlayout.widget.ConstraintLayout
@@ -39,6 +38,8 @@
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LongPressHandlingViewLogger
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.KeyguardBlueprintLog
 import com.android.systemui.log.dagger.LongPressTouchLog
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
@@ -49,10 +50,8 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Includes the device entry icon. */
-@ExperimentalCoroutinesApi
 class DefaultDeviceEntrySection
 @Inject
 constructor(
@@ -68,7 +67,9 @@
     private val falsingManager: Lazy<FalsingManager>,
     private val vibratorHelper: Lazy<VibratorHelper>,
     @LongPressTouchLog private val logBuffer: LogBuffer,
+    @KeyguardBlueprintLog blueprintLogBuffer: LogBuffer,
 ) : KeyguardSection() {
+    private val blueprintLogger = Logger(blueprintLogBuffer, TAG)
     private val deviceEntryIconViewId = R.id.device_entry_icon_view
     private var disposableHandle: DisposableHandle? = null
 
@@ -101,11 +102,8 @@
     }
 
     override fun applyConstraints(constraintSet: ConstraintSet) {
-        Log.d(
-            "DefaultDeviceEntrySection",
-            "isUdfpsSupported=${deviceEntryIconViewModel.get().isUdfpsSupported.value}",
-        )
         val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value
+        blueprintLogger.d({ "isUdfpsSupported=$bool1" }) { bool1 = isUdfpsSupported }
 
         val scaleFactor: Float = authController.scaleFactor
         val mBottomPaddingPx =
@@ -126,12 +124,13 @@
 
         if (isUdfpsSupported) {
             deviceEntryIconViewModel.get().udfpsLocation.value?.let { udfpsLocation ->
-                Log.d(
-                    "DeviceEntrySection",
-                    "udfpsLocation=$udfpsLocation, " +
-                        "scaledLocation=(${udfpsLocation.centerX},${udfpsLocation.centerY}), " +
-                        "unusedAuthController=${authController.udfpsLocation}",
-                )
+                blueprintLogger.d({
+                    "udfpsLocation=$str1, scaledLocation=$str2, unusedAuthController=$str3"
+                }) {
+                    str1 = "$udfpsLocation"
+                    str2 = "(${udfpsLocation.centerX}, ${udfpsLocation.centerY})"
+                    str3 = "${authController.udfpsLocation}"
+                }
                 centerIcon(
                     Point(udfpsLocation.centerX.toInt(), udfpsLocation.centerY.toInt()),
                     udfpsLocation.radius,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
index 8186aa3..c48fc53 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
@@ -27,10 +27,8 @@
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Positions the UDFPS accessibility overlay on the bottom half of the keyguard. */
-@ExperimentalCoroutinesApi
 class DefaultUdfpsAccessibilityOverlaySection
 @Inject
 constructor(
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 29bda76..fdd9355 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
@@ -24,7 +24,6 @@
 import android.transition.TransitionListenerAdapter
 import android.transition.TransitionSet
 import android.transition.TransitionValues
-import android.util.Log
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewTreeObserver.OnPreDrawListener
@@ -35,6 +34,9 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS
 import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_UP_MILLIS
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.plugins.clocks.ClockLogger.Companion.getVisText
 import com.android.systemui.res.R
 import com.android.systemui.shared.R as sharedR
 import com.google.android.material.math.MathUtils
@@ -48,19 +50,21 @@
 class ClockSizeTransition(
     config: IntraBlueprintTransition.Config,
     clockViewModel: KeyguardClockViewModel,
+    logBuffer: LogBuffer,
 ) : TransitionSet() {
+
     init {
         ordering = ORDERING_TOGETHER
         if (config.type != Type.SmartspaceVisibility) {
-            addTransition(ClockFaceOutTransition(config, clockViewModel))
-            addTransition(ClockFaceInTransition(config, clockViewModel))
+            addTransition(ClockFaceOutTransition(config, clockViewModel, logBuffer))
+            addTransition(ClockFaceInTransition(config, clockViewModel, logBuffer))
         }
-        addTransition(SmartspaceMoveTransition(config, clockViewModel))
+        addTransition(SmartspaceMoveTransition(config, clockViewModel, logBuffer))
     }
 
-    abstract class VisibilityBoundsTransition() : Transition() {
+    abstract class VisibilityBoundsTransition(logBuffer: LogBuffer) : Transition() {
+        protected val logger = Logger(logBuffer, this::class.simpleName!!)
         abstract val captureSmartspace: Boolean
-        protected val TAG = this::class.simpleName!!
 
         override fun captureEndValues(transition: TransitionValues) = captureValues(transition)
 
@@ -80,7 +84,9 @@
                 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 under $parent")
+                logger.e({ "Failed to find smartspace equivalent target under $str1" }) {
+                    str1 = "$parent"
+                }
                 return
             }
             transition.values[SMARTSPACE_BOUNDS] = targetSSView.getRect()
@@ -143,10 +149,10 @@
             endValues: TransitionValues?,
         ): Animator? {
             if (startValues == null || endValues == null) {
-                Log.w(
-                    TAG,
-                    "Couldn't create animator: startValues=$startValues; endValues=$endValues",
-                )
+                logger.w({ "Couldn't create animator: startValues=$str1; endValues=$str2" }) {
+                    str1 = "$startValues"
+                    str2 = "$endValues"
+                }
                 return null
             }
 
@@ -156,15 +162,18 @@
             mutateTargets(from, to)
 
             if (from.isVisible == to.isVisible && from.bounds.equals(to.bounds)) {
-                if (DEBUG) {
-                    Log.w(
-                        TAG,
-                        "Skipping no-op transition: ${to.view}; " +
-                            "vis: ${from.visibility} -> ${to.visibility}; " +
-                            "alpha: ${from.alpha} -> ${to.alpha}; " +
-                            "bounds: ${from.bounds} -> ${to.bounds}; ",
-                    )
+                logger.w({
+                    "Skipping no-op transition: $str1; " +
+                        "vis: ${getVisText(int1)} -> ${getVisText(int2)}; " +
+                        "alpha: $str2; bounds: $str3; "
+                }) {
+                    str1 = "${to.view}"
+                    int1 = from.visibility
+                    int2 = to.visibility
+                    str2 = "${from.alpha} -> ${to.alpha}"
+                    str3 = "${from.bounds} -> ${to.bounds}"
                 }
+
                 return null
             }
 
@@ -179,15 +188,27 @@
                     lerp(from.bounds.bottom, to.bounds.bottom, fract),
                 )
 
-            fun assignAnimValues(src: String, fract: Float, vis: Int? = null) {
+            fun assignAnimValues(
+                src: String,
+                fract: Float,
+                vis: Int? = null,
+                log: Boolean = false,
+            ) {
                 mutateTargets(from, to)
                 val bounds = computeBounds(fract)
                 val alpha = MathUtils.lerp(from.alpha, to.alpha, fract)
-                if (DEBUG) {
-                    Log.i(
-                        TAG,
-                        "$src: ${to.view}; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;",
-                    )
+                if (log) {
+                    logger.i({
+                        "$str1: $str2; fract=$int1%; alpha=$double1; " +
+                            "vis=${getVisText(int2)}; bounds=$str3;"
+                    }) {
+                        str1 = src
+                        str2 = "${to.view}"
+                        int1 = (fract * 100).toInt()
+                        double1 = alpha.toDouble()
+                        int2 = vis ?: View.VISIBLE
+                        str3 = "$bounds"
+                    }
                 }
 
                 to.view.setVisibility(vis ?: View.VISIBLE)
@@ -195,14 +216,15 @@
                 to.view.setRect(bounds)
             }
 
-            if (DEBUG) {
-                Log.i(
-                    TAG,
-                    "transitioning: ${to.view}; " +
-                        "vis: ${from.visibility} -> ${to.visibility}; " +
-                        "alpha: ${from.alpha} -> ${to.alpha}; " +
-                        "bounds: ${from.bounds} -> ${to.bounds}; ",
-                )
+            logger.i({
+                "transitioning: $str1; vis: ${getVisText(int1)} -> ${getVisText(int2)}; " +
+                    "alpha: $str2; bounds: $str3;"
+            }) {
+                str1 = "${to.view}"
+                int1 = from.visibility
+                int2 = to.visibility
+                str2 = "${from.alpha} -> ${to.alpha}"
+                str3 = "${from.bounds} -> ${to.bounds}"
             }
 
             return ValueAnimator.ofFloat(0f, 1f).also { anim ->
@@ -210,7 +232,7 @@
                 // predraw listener. This is suboptimal but prevents issues with layout passes
                 // overwriting the animation for individual frames.
                 val predrawCallback = OnPreDrawListener {
-                    assignAnimValues("predraw", anim.animatedFraction)
+                    assignAnimValues("predraw", anim.animatedFraction, log = false)
                     return@OnPreDrawListener true
                 }
 
@@ -229,17 +251,17 @@
                 val listener =
                     object : AnimatorListenerAdapter() {
                         override fun onAnimationStart(anim: Animator) {
-                            assignAnimValues("start", 0f, from.visibility)
+                            assignAnimValues("start", 0f, from.visibility, log = true)
                         }
 
                         override fun onAnimationEnd(anim: Animator) {
-                            assignAnimValues("end", 1f, to.visibility)
+                            assignAnimValues("end", 1f, to.visibility, log = true)
                             if (sendToBack) to.view.translationZ = 0f
                         }
                     }
 
                 anim.addListener(listener)
-                assignAnimValues("init", 0f, from.visibility)
+                assignAnimValues("init", 0f, from.visibility, log = true)
             }
         }
 
@@ -256,7 +278,8 @@
     abstract class ClockFaceTransition(
         config: IntraBlueprintTransition.Config,
         val viewModel: KeyguardClockViewModel,
-    ) : VisibilityBoundsTransition() {
+        logBuffer: LogBuffer,
+    ) : VisibilityBoundsTransition(logBuffer) {
         protected abstract val isLargeClock: Boolean
         protected abstract val smallClockMoveScale: Float
         override val captureSmartspace
@@ -265,15 +288,17 @@
         protected fun addTargets() {
             if (isLargeClock) {
                 viewModel.currentClock.value?.let {
-                    if (DEBUG) Log.i(TAG, "Adding large clock views: ${it.largeClock.layout.views}")
+                    logger.i({ "Adding large clock views: $str1" }) {
+                        str1 = "${it.largeClock.layout.views}"
+                    }
                     it.largeClock.layout.views.forEach { addTarget(it) }
                 }
                     ?: run {
-                        Log.e(TAG, "No large clock set, falling back")
+                        logger.e("No large clock set, falling back")
                         addTarget(customR.id.lockscreen_clock_view_large)
                     }
             } else {
-                if (DEBUG) Log.i(TAG, "Adding small clock")
+                logger.i("Adding small clock")
                 addTarget(customR.id.lockscreen_clock_view)
             }
         }
@@ -294,7 +319,7 @@
                 from.bounds.top = to.bounds.top - ssTranslation
                 from.bounds.bottom = to.bounds.bottom - ssTranslation
             } else {
-                Log.e(TAG, "initTargets: smallClock received no smartspace bounds")
+                logger.e("initTargets: smallClock received no smartspace bounds")
             }
         }
     }
@@ -302,7 +327,8 @@
     class ClockFaceInTransition(
         config: IntraBlueprintTransition.Config,
         viewModel: KeyguardClockViewModel,
-    ) : ClockFaceTransition(config, viewModel) {
+        logBuffer: LogBuffer,
+    ) : ClockFaceTransition(config, viewModel, logBuffer) {
         override val isLargeClock = viewModel.isLargeClockVisible.value
         override val smallClockMoveScale = CLOCK_IN_MILLIS / STATUS_AREA_MOVE_DOWN_MILLIS.toFloat()
 
@@ -323,7 +349,8 @@
     class ClockFaceOutTransition(
         config: IntraBlueprintTransition.Config,
         viewModel: KeyguardClockViewModel,
-    ) : ClockFaceTransition(config, viewModel) {
+        logBuffer: LogBuffer,
+    ) : ClockFaceTransition(config, viewModel, logBuffer) {
         override val isLargeClock = !viewModel.isLargeClockVisible.value
         override val smallClockMoveScale = CLOCK_OUT_MILLIS / STATUS_AREA_MOVE_UP_MILLIS.toFloat()
 
@@ -342,7 +369,8 @@
     class SmartspaceMoveTransition(
         val config: IntraBlueprintTransition.Config,
         val viewModel: KeyguardClockViewModel,
-    ) : VisibilityBoundsTransition() {
+        logBuffer: LogBuffer,
+    ) : VisibilityBoundsTransition(logBuffer) {
         private val isLargeClock = viewModel.isLargeClockVisible.value
         override val captureSmartspace = false
 
@@ -361,7 +389,7 @@
         override fun initTargets(from: Target, to: Target) {
             // If view is changing visibility, hold it in place
             if (from.isVisible == to.isVisible) return
-            if (DEBUG) Log.i(TAG, "Holding position of ${to.view.id}")
+            logger.i({ "Holding position of $int1" }) { int1 = to.view.id }
 
             if (from.isVisible) {
                 to.bounds.set(from.bounds)
@@ -383,8 +411,4 @@
             const val STATUS_AREA_MOVE_DOWN_MILLIS = 467L
         }
     }
-
-    companion object {
-        val DEBUG = false
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
index 9f8e9c5..848bcab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
@@ -25,10 +25,8 @@
 import com.android.systemui.statusbar.gesture.TapGestureDetector
 import dagger.Lazy
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Provides dependencies for the AlternateBouncerViewBinder. */
-@ExperimentalCoroutinesApi
 class AlternateBouncerDependencies
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
index bbe5fed..06e6ee48 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.statusbar.KeyguardIndicationController.DEFAULT_MESSAGE_TIME
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterIsInstance
@@ -38,7 +37,6 @@
 import kotlinx.coroutines.flow.onStart
 
 /** View model for the alternate bouncer message area. */
-@ExperimentalCoroutinesApi
 class AlternateBouncerMessageAreaViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
index 992550c..4558681a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 
@@ -34,7 +33,6 @@
  * Breaks down ALTERNATE BOUNCER->AOD transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class AlternateBouncerToAodTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
index 55a48b6..9a3cb7f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 
@@ -33,7 +32,6 @@
  * Breaks down ALTERNATE BOUNCER->DOZING transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class AlternateBouncerToDozingTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
index e9db1d2..f42d74e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
@@ -29,14 +29,12 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /**
  * Breaks down ALTERNATE_BOUNCER->GONE transition into discrete steps for corresponding views to
  * consume.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class AlternateBouncerToGoneTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModel.kt
index b04521c..c6ba441 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModel.kt
@@ -26,14 +26,12 @@
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /**
  * Breaks down ALTERNATE_BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views
  * to consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class AlternateBouncerToLockscreenTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
index c49e783..c1ed7a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
@@ -24,14 +24,12 @@
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /**
  * Breaks down ALTERNATE_BOUNCER->OCCLUDED transition into discrete steps for corresponding views to
  * consume.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class AlternateBouncerToOccludedTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
index 733d7d7..43cce4b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
@@ -24,14 +24,12 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.BlurConfig
-import com.android.systemui.keyguard.ui.transitions.BlurConfig.Companion.maxBlurRadiusToNotificationPanelBlurRadius
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 
@@ -39,7 +37,6 @@
  * Breaks down ALTERNATE BOUNCER->PRIMARY BOUNCER transition into discrete steps for corresponding
  * views to consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class AlternateBouncerToPrimaryBouncerTransitionViewModel
 @Inject
@@ -89,9 +86,7 @@
             shadeDependentFlows.transitionFlow(
                 flowWhenShadeIsNotExpanded = emptyFlow(),
                 flowWhenShadeIsExpanded =
-                    transitionAnimation.immediatelyTransitionTo(
-                        blurConfig.maxBlurRadiusPx.maxBlurRadiusToNotificationPanelBlurRadius()
-                    ),
+                    transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
             )
         } else {
             emptyFlow<Float>()
@@ -103,7 +98,11 @@
     override val windowBlurRadius: Flow<Float> =
         shadeDependentFlows.transitionFlow(
             flowWhenShadeIsExpanded =
-                transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
+                if (Flags.notificationShadeBlur()) {
+                    transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+                } else {
+                    emptyFlow()
+                },
             flowWhenShadeIsNotExpanded =
                 transitionAnimation.sharedFlow(
                     duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index 3a5263f..acd381e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.shared.recents.utilities.Utilities.clamp
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.emptyFlow
@@ -37,7 +36,6 @@
 import kotlinx.coroutines.flow.onStart
 
 /** Models the UI state for the UDFPS icon view in the alternate bouncer view. */
-@ExperimentalCoroutinesApi
 class AlternateBouncerUdfpsIconViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index b5d9e2a..1ca08fe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -27,14 +27,12 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import dagger.Lazy
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 
-@ExperimentalCoroutinesApi
 class AlternateBouncerViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
index 5cf100e..94f53b4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
@@ -20,14 +20,12 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 
-@ExperimentalCoroutinesApi
 class AlternateBouncerWindowViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index fb311a5..aed8664 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.util.Log
@@ -37,7 +35,6 @@
 import javax.inject.Inject
 import kotlin.math.max
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
index 8e8b09d..4802aba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -27,11 +27,9 @@
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /** Breaks down AOD->GONE transition into discrete steps for corresponding views to consume. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class AodToGoneTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index 6eeab8d..3c35566 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -29,13 +29,11 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /**
  * Breaks down AOD->LOCKSCREEN transition into discrete steps for corresponding views to consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class AodToLockscreenTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
index 26bf0bc..8b5e3b9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 
@@ -36,7 +35,6 @@
  * Breaks down AOD->PRIMARY BOUNCER transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class AodToPrimaryBouncerTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
index 1edfec8..59cc157 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -32,14 +32,12 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlin.time.Duration
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 
 /** ALTERNATE and PRIMARY bouncers common animations */
-@OptIn(ExperimentalCoroutinesApi::class)
 class BouncerToGoneFlows
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 29ae4b9..75bba48 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
@@ -35,7 +34,6 @@
 
 /** Models the UI state for the device entry icon background view. */
 @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA")
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DeviceEntryBackgroundViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 1965252..5121b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 import kotlin.math.roundToInt
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -43,7 +42,6 @@
 
 /** Models the UI state for the device entry icon foreground view (displayed icon). */
 @OptIn(FlowPreview::class)
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DeviceEntryForegroundViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index b1a2ec9..13cd583 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -38,7 +38,6 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -55,7 +54,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** Models the UI state for the containing device entry icon & long-press handling view. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DeviceEntryIconViewModel
 @Inject
@@ -264,16 +262,6 @@
         deviceEntrySourceInteractor.attemptEnterDeviceFromDeviceEntryIcon()
     }
 
-    private fun DeviceEntryIconView.IconType.toAccessibilityHintType():
-        DeviceEntryIconView.AccessibilityHintType {
-        return when (this) {
-            DeviceEntryIconView.IconType.FINGERPRINT,
-            DeviceEntryIconView.IconType.LOCK -> DeviceEntryIconView.AccessibilityHintType.BOUNCER
-            DeviceEntryIconView.IconType.UNLOCK -> DeviceEntryIconView.AccessibilityHintType.ENTER
-            DeviceEntryIconView.IconType.NONE -> DeviceEntryIconView.AccessibilityHintType.NONE
-        }
-    }
-
     companion object {
         const val UNLOCKED_DELAY_MS = 50L
     }
@@ -284,3 +272,13 @@
     val y: Int, // current y burn in offset based on the aodTransitionAmount
     val progress: Float, // current progress based on the aodTransitionAmount
 )
+
+fun DeviceEntryIconView.IconType.toAccessibilityHintType():
+    DeviceEntryIconView.AccessibilityHintType {
+    return when (this) {
+        DeviceEntryIconView.IconType.FINGERPRINT,
+        DeviceEntryIconView.IconType.LOCK -> DeviceEntryIconView.AccessibilityHintType.BOUNCER
+        DeviceEntryIconView.IconType.UNLOCK -> DeviceEntryIconView.AccessibilityHintType.ENTER
+        DeviceEntryIconView.IconType.NONE -> DeviceEntryIconView.AccessibilityHintType.NONE
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.kt
index 1e42e19..13a0e8e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.kt
@@ -17,12 +17,14 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
 import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -31,7 +33,10 @@
 @SysUISingleton
 class DozingToGlanceableHubTransitionViewModel
 @Inject
-constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
+constructor(
+    animationFlow: KeyguardTransitionAnimationFlow,
+    private val blurFactory: GlanceableHubBlurComponent.Factory,
+) : DeviceEntryIconTransition, GlanceableHubTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
@@ -48,4 +53,7 @@
      * power button when dozing and docked.
      */
     val notificationAlpha: Flow<Float> = flowOf(0f)
+
+    override val windowBlurRadius: Flow<Float> =
+        blurFactory.create(transitionAnimation).getBlurProvider().enterBlurRadius
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
index 480f948..52ea419 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
@@ -26,11 +26,9 @@
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /** Breaks down DOZING->GONE transition into discrete steps for corresponding views to consume. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DozingToGoneTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
index 9d8a7a8..672ed52 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -25,13 +25,11 @@
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /**
  * Breaks down DOZING->LOCKSCREEN transition into discrete steps for corresponding views to consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DozingToLockscreenTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
index d9ca267..ff96e03 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 
@@ -36,7 +35,6 @@
  * Breaks down DOZING->PRIMARY BOUNCER transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DozingToPrimaryBouncerTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
index 7562392..7c26e0b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
@@ -26,13 +26,11 @@
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
 
 /** Breaks down DREAMING->AOD transition into discrete steps for corresponding views to consume. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DreamingToAodTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
index c9fdf7a..4e07ca0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
@@ -20,30 +20,31 @@
 import com.android.app.animation.Interpolators.EMPHASIZED
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class DreamingToGlanceableHubTransitionViewModel
 @Inject
 constructor(
     animationFlow: KeyguardTransitionAnimationFlow,
     @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
-) : DeviceEntryIconTransition {
+    private val blurFactory: GlanceableHubBlurComponent.Factory,
+) : DeviceEntryIconTransition, GlanceableHubTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
@@ -101,4 +102,7 @@
     private companion object {
         val TO_GLANCEABLE_HUB_DURATION = 1.seconds
     }
+
+    override val windowBlurRadius: Flow<Float> =
+        blurFactory.create(transitionAnimation).getBlurProvider().enterBlurRadius
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index 10605b2..5dd6d2e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -26,14 +26,12 @@
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /**
  * Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class DreamingToLockscreenTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDozingTransitionViewModel.kt
new file mode 100644
index 0000000..6738b27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDozingTransitionViewModel.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
+import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class GlanceableHubToDozingTransitionViewModel
+@Inject
+constructor(
+    animationFlow: KeyguardTransitionAnimationFlow,
+    private val blurComponentFactory: GlanceableHubBlurComponent.Factory,
+) : GlanceableHubTransition {
+    private val transitionAnimation =
+        animationFlow
+            .setup(
+                duration = TO_GLANCEABLE_HUB_DURATION,
+                edge = Edge.create(DOZING, Scenes.Communal),
+            )
+            .setupWithoutSceneContainer(edge = Edge.create(GLANCEABLE_HUB, DOZING))
+
+    override val windowBlurRadius: Flow<Float> =
+        blurComponentFactory.create(transitionAnimation).getBlurProvider().exitBlurRadius
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
index 723fba6..2b8ca8a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
@@ -20,30 +20,31 @@
 import com.android.app.animation.Interpolators
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class GlanceableHubToDreamingTransitionViewModel
 @Inject
 constructor(
     animationFlow: KeyguardTransitionAnimationFlow,
     @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
-) : DeviceEntryIconTransition {
+    private val blurFactory: GlanceableHubBlurComponent.Factory,
+) : DeviceEntryIconTransition, GlanceableHubTransition {
 
     private val transitionAnimation =
         animationFlow
@@ -96,6 +97,9 @@
             onFinish = { 0f },
         )
 
+    override val windowBlurRadius: Flow<Float> =
+        blurFactory.create(transitionAnimation).getBlurProvider().exitBlurRadius
+
     private companion object {
         val FROM_GLANCEABLE_HUB_DURATION = 1.seconds
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index 5a4d0689..b4b4c82 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -20,18 +20,19 @@
 import com.android.app.animation.Interpolators.EMPHASIZED
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
 import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flatMapLatest
@@ -41,14 +42,14 @@
  * Breaks down GLANCEABLE_HUB->LOCKSCREEN transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class GlanceableHubToLockscreenTransitionViewModel
 @Inject
 constructor(
     @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
-) {
+    private val blurFactory: GlanceableHubBlurComponent.Factory,
+) : GlanceableHubTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
@@ -57,6 +58,9 @@
             )
             .setupWithoutSceneContainer(edge = Edge.create(from = GLANCEABLE_HUB, to = LOCKSCREEN))
 
+    override val windowBlurRadius: Flow<Float> =
+        blurFactory.create(transitionAnimation).getBlurProvider().exitBlurRadius
+
     val keyguardAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
             duration = 167.milliseconds,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
index cd98bb0..c3a412a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
@@ -17,12 +17,14 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
 import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_OCCLUDED_DURATION
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -32,7 +34,8 @@
 @Inject
 constructor(
     animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+    private val blurFactory: GlanceableHubBlurComponent.Factory,
+) : DeviceEntryIconTransition, GlanceableHubTransition {
 
     private val transitionAnimation =
         animationFlow
@@ -44,4 +47,7 @@
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
+
+    override val windowBlurRadius: Flow<Float> =
+        blurFactory.create(transitionAnimation).getBlurProvider().exitBlurRadius
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt
index 5ab4583..4001054 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.Flags
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -26,12 +29,17 @@
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
 
 @SysUISingleton
 class GlanceableHubToPrimaryBouncerTransitionViewModel
 @Inject
-constructor(private val blurConfig: BlurConfig, animationFlow: KeyguardTransitionAnimationFlow) :
-    PrimaryBouncerTransition {
+constructor(
+    private val blurConfig: BlurConfig,
+    animationFlow: KeyguardTransitionAnimationFlow,
+    communalSettingsInteractor: CommunalSettingsInteractor,
+) : PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
@@ -41,7 +49,15 @@
             .setupWithoutSceneContainer(edge = Edge.create(GLANCEABLE_HUB, PRIMARY_BOUNCER))
 
     override val windowBlurRadius: Flow<Float> =
-        transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+        if (Flags.glanceableHubBlurredBackground()) {
+            communalSettingsInteractor.communalBackground
+                .filter { it != CommunalBackgroundType.BLUR }
+                .flatMapLatest {
+                    transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+                }
+        } else {
+            transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+        }
 
     override val notificationBlurRadius: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0.0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 43872b7..bedcf14 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -32,14 +32,12 @@
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.transform
 
 /** Breaks down GONE->AOD transition into discrete steps for corresponding views to consume. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class GoneToAodTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
index c62e4f4..45c3daa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
@@ -27,13 +27,11 @@
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
 
 /** Breaks down GONE->DOZING transition into discrete steps for corresponding views to consume. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class GoneToDozingTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
index 1289036..d7be356 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
@@ -20,7 +20,6 @@
 import android.os.Handler
 import android.transition.Transition
 import android.transition.TransitionManager
-import android.util.Log
 import androidx.constraintlayout.widget.ConstraintLayout
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
@@ -29,6 +28,9 @@
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.KeyguardBlueprintLog
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -41,7 +43,9 @@
     @Main private val handler: Handler,
     private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    @KeyguardBlueprintLog private val blueprintLog: LogBuffer,
 ) {
+    private val logger = Logger(blueprintLog, "KeyguardBlueprintViewModel")
     val blueprint = keyguardBlueprintInteractor.blueprint
     val blueprintId = keyguardBlueprintInteractor.blueprintId
     val refreshTransition = keyguardBlueprintInteractor.refreshTransition
@@ -53,27 +57,27 @@
     private val transitionListener =
         object : Transition.TransitionListener {
             override fun onTransitionCancel(transition: Transition) {
-                if (DEBUG) Log.w(TAG, "onTransitionCancel: ${transition::class.simpleName}")
+                logger.w({ "onTransitionCancel: $str1" }) { str1 = transition::class.simpleName }
                 updateTransitions(null) { remove(transition) }
             }
 
             override fun onTransitionEnd(transition: Transition) {
-                if (DEBUG) Log.i(TAG, "onTransitionEnd: ${transition::class.simpleName}")
+                logger.i({ "onTransitionEnd: $str1" }) { str1 = transition::class.simpleName }
                 updateTransitions(null) { remove(transition) }
             }
 
             override fun onTransitionPause(transition: Transition) {
-                if (DEBUG) Log.i(TAG, "onTransitionPause: ${transition::class.simpleName}")
+                logger.i({ "onTransitionPause: $str1" }) { str1 = transition::class.simpleName }
                 updateTransitions(null) { remove(transition) }
             }
 
             override fun onTransitionResume(transition: Transition) {
-                if (DEBUG) Log.i(TAG, "onTransitionResume: ${transition::class.simpleName}")
+                logger.i({ "onTransitionResume: $str1" }) { str1 = transition::class.simpleName }
                 updateTransitions(null) { add(transition) }
             }
 
             override fun onTransitionStart(transition: Transition) {
-                if (DEBUG) Log.i(TAG, "onTransitionStart: ${transition::class.simpleName}")
+                logger.i({ "onTransitionStart: $str1" }) { str1 = transition::class.simpleName }
                 updateTransitions(null) { add(transition) }
             }
         }
@@ -104,7 +108,7 @@
 
         runTransition(
             constraintLayout,
-            IntraBlueprintTransition(newConfig, clockViewModel, smartspaceViewModel),
+            IntraBlueprintTransition(newConfig, clockViewModel, smartspaceViewModel, blueprintLog),
             config,
             apply,
         )
@@ -118,12 +122,10 @@
     ) {
         val currentPriority = currentTransition.value?.let { it.config.type.priority } ?: -1
         if (config.checkPriority && config.type.priority < currentPriority) {
-            if (DEBUG) {
-                Log.w(
-                    TAG,
-                    "runTransition: skipping ${transition::class.simpleName}: " +
-                        "currentPriority=$currentPriority; config=$config",
-                )
+            logger.w({ "runTransition: skipping $str1: currentPriority=$int1; config=$str2" }) {
+                str1 = transition::class.simpleName
+                int1 = currentPriority
+                str2 = "$config"
             }
             apply()
             return
@@ -137,12 +139,10 @@
                 config
             }
 
-        if (DEBUG) {
-            Log.i(
-                TAG,
-                "runTransition: running ${transition::class.simpleName}: " +
-                    "currentPriority=$currentPriority; config=$newConfig",
-            )
+        logger.i({ "runTransition: running $str1: currentPriority=$int1; config=$str2" }) {
+            str1 = transition::class.simpleName
+            int1 = currentPriority
+            str2 = "$newConfig"
         }
 
         // beginDelayedTransition makes a copy, so we temporarially add the uncopied transition to
@@ -162,9 +162,4 @@
             handler.post { updateTransitions(null) { remove(transition) } }
         }
     }
-
-    companion object {
-        private const val TAG = "KeyguardBlueprintViewModel"
-        private const val DEBUG = false
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index 3dcc60a..8849d24 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -33,7 +33,6 @@
 import javax.inject.Inject
 import javax.inject.Named
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -75,7 +74,6 @@
             }
             .distinctUntilChanged()
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     private val burnIn: Flow<BurnInModel> =
         combine(
                 burnInInteractor.burnIn(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModel.kt
index e68e465..ba03c48 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModel.kt
@@ -16,10 +16,50 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.compose.runtime.getValue
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
-import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.flowOf
 
-class KeyguardMediaViewModel @Inject constructor(mediaCarouselInteractor: MediaCarouselInteractor) {
-    val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasActiveMediaOrRecommendation
+class KeyguardMediaViewModel
+@AssistedInject
+constructor(
+    mediaCarouselInteractor: MediaCarouselInteractor,
+    keyguardInteractor: KeyguardInteractor,
+) : ExclusiveActivatable() {
+
+    private val hydrator = Hydrator("KeyguardMediaViewModel.hydrator")
+    /**
+     * Whether media carousel is visible on lockscreen. Media may be presented on lockscreen but
+     * still hidden on certain surfaces like AOD
+     */
+    val isMediaVisible: Boolean by
+        hydrator.hydratedStateOf(
+            traceName = "isMediaVisible",
+            source =
+                keyguardInteractor.isDozing.flatMapLatestConflated { isDozing ->
+                    if (isDozing) {
+                        flowOf(false)
+                    } else {
+                        mediaCarouselInteractor.hasActiveMediaOrRecommendation
+                    }
+                },
+            initialValue =
+                !keyguardInteractor.isDozing.value &&
+                    mediaCarouselInteractor.hasActiveMediaOrRecommendation.value,
+        )
+
+    override suspend fun onActivated(): Nothing {
+        hydrator.activate()
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): KeyguardMediaViewModel
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index df3c782..7a9e55a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -34,7 +34,6 @@
 import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -45,7 +44,6 @@
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.stateIn
 
-@OptIn(ExperimentalCoroutinesApi::class)
 class KeyguardQuickAffordancesCombinedViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index e51e05b..11a509a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.PulseExpansionInteractor
+import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
@@ -56,7 +57,6 @@
 import javax.inject.Inject
 import kotlin.math.max
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -69,7 +69,6 @@
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardRootViewModel
 @Inject
@@ -133,6 +132,7 @@
     private val screenOffAnimationController: ScreenOffAnimationController,
     private val aodBurnInViewModel: AodBurnInViewModel,
     private val shadeInteractor: ShadeInteractor,
+    wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor,
 ) {
     val burnInLayerVisibility: Flow<Int> =
         keyguardTransitionInteractor.startedKeyguardTransitionStep
@@ -307,6 +307,16 @@
             BurnInScaleViewModel(scale = it.scale, scaleClockOnly = it.scaleClockOnly)
         }
 
+    val isAodPromotedNotifVisible: StateFlow<Boolean> =
+        keyguardTransitionInteractor
+            .transitionValue(AOD)
+            .map { it == 1f }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false,
+            )
+
     /** Is the notification icon container visible? */
     val isNotifIconContainerVisible: StateFlow<AnimatedValue<Boolean>> =
         combine(
@@ -362,6 +372,8 @@
                 initialValue = AnimatedValue.NotAnimating(false),
             )
 
+    val shouldSendFocalArea = wallpaperFocalAreaInteractor.shouldSendFocalArea
+
     fun onNotificationContainerBoundsChanged(top: Float, bottom: Float, animate: Boolean = false) {
         keyguardInteractor.setNotificationContainerBounds(
             NotificationContainerBounds(top = top, bottom = bottom, isAnimated = animate)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
index 36a342b..4584ea2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
@@ -19,6 +19,7 @@
 
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTouchHandlingInteractor
 import com.android.systemui.res.R
 import javax.inject.Inject
@@ -29,19 +30,18 @@
 @Inject
 constructor(
     private val interactor: KeyguardTouchHandlingInteractor,
+    configurationInteractor: ConfigurationInteractor,
 ) {
     val isVisible: Flow<Boolean> = interactor.isMenuVisible
     val shouldOpenSettings: Flow<Boolean> = interactor.shouldOpenSettings
 
-    val icon: Icon =
-        Icon.Resource(
-            res = R.drawable.ic_palette,
-            contentDescription = null,
-        )
+    val icon: Icon = Icon.Resource(res = R.drawable.ic_palette, contentDescription = null)
 
-    val text: Text =
-        Text.Resource(
-            res = R.string.lock_screen_settings,
+    val text: Text = Text.Resource(res = R.string.lock_screen_settings)
+
+    val textSize =
+        configurationInteractor.dimensionPixelSize(
+            com.android.internal.R.dimen.text_size_small_material
         )
 
     fun onTouchGestureStarted() {
@@ -49,9 +49,7 @@
     }
 
     fun onTouchGestureEnded(isClick: Boolean) {
-        interactor.onMenuTouchGestureEnded(
-            isClick = isClick,
-        )
+        interactor.onMenuTouchGestureEnded(isClick = isClick)
     }
 
     fun onSettingsShown() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
index 6d1aefe..16e32df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
@@ -19,7 +19,6 @@
 import com.android.systemui.keyguard.domain.interactor.LightRevealScrimInteractor
 import com.android.systemui.statusbar.LightRevealEffect
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
 
@@ -27,7 +26,6 @@
  * Models UI state for the light reveal scrim, which is used during screen on and off animations to
  * draw a gradient that reveals/hides the contents of the screen.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 class LightRevealScrimViewModel
 @Inject
 constructor(private val interactor: LightRevealScrimInteractor) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 2f21ebc..6b4f4c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.transform
@@ -40,7 +39,6 @@
 /**
  * Breaks down LOCKSCREEN->AOD transition into discrete steps for corresponding views to consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class LockscreenToAodTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index 7abf35d..dc7fefa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -26,11 +26,9 @@
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class LockscreenToDozingTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
index acaa9b9..d4cad71 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
@@ -20,18 +20,19 @@
 import com.android.app.animation.Interpolators.EMPHASIZED
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flatMapLatest
@@ -41,14 +42,15 @@
  * Breaks down LOCKSCREEN->GLANCEABLE_HUB transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class LockscreenToGlanceableHubTransitionViewModel
 @Inject
 constructor(
     @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
-) {
+    private val blurFactory: GlanceableHubBlurComponent.Factory,
+) : GlanceableHubTransition {
+
     private val transitionAnimation =
         animationFlow
             .setup(
@@ -57,6 +59,9 @@
             )
             .setupWithoutSceneContainer(edge = Edge.create(from = LOCKSCREEN, to = GLANCEABLE_HUB))
 
+    override val windowBlurRadius: Flow<Float> =
+        blurFactory.create(transitionAnimation).getBlurProvider().enterBlurRadius
+
     val keyguardAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
             duration = 167.milliseconds,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
index a249793..a31bc75 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -30,13 +30,11 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /**
  * Breaks down LOCKSCREEN->GONE transition into discrete steps for corresponding views to consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class LockscreenToGoneTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index 44c4c87..b96c879 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.BlurConfig
-import com.android.systemui.keyguard.ui.transitions.BlurConfig.Companion.maxBlurRadiusToNotificationPanelBlurRadius
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -32,7 +31,6 @@
 import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 
@@ -40,7 +38,6 @@
  * Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class LockscreenToPrimaryBouncerTransitionViewModel
 @Inject
@@ -88,9 +85,7 @@
             shadeDependentFlows.transitionFlow(
                 flowWhenShadeIsNotExpanded = emptyFlow(),
                 flowWhenShadeIsExpanded =
-                    transitionAnimation.immediatelyTransitionTo(
-                        blurConfig.maxBlurRadiusPx.maxBlurRadiusToNotificationPanelBlurRadius()
-                    ),
+                    transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
             )
         } else {
             emptyFlow()
@@ -110,7 +105,11 @@
     override val windowBlurRadius: Flow<Float> =
         shadeDependentFlows.transitionFlow(
             flowWhenShadeIsExpanded =
-                transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
+                if (Flags.notificationShadeBlur()) {
+                    transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+                } else {
+                    emptyFlow()
+                },
             flowWhenShadeIsNotExpanded =
                 transitionAnimation.sharedFlow(
                     duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt
index 618b047..b7e3e2b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.compose.animation.scene.Swipe
@@ -33,7 +31,6 @@
 import com.android.systemui.shade.ui.viewmodel.splitShadeActions
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAlternateBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAlternateBouncerTransitionViewModel.kt
index 5bfcccb..092031b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAlternateBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAlternateBouncerTransitionViewModel.kt
@@ -24,14 +24,12 @@
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /**
  * Breaks down OCCLUDED->ALTERNATE_BOUNCER transition into discrete steps for corresponding views to
  * consume.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class OccludedToAlternateBouncerTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
index 706a3c4..a8376ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
@@ -26,13 +26,11 @@
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
 
 /** Breaks down OCCLUDED->AOD transition into discrete steps for corresponding views to consume. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class OccludedToAodTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
index 4fb2b9b..1ead9c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -34,7 +33,6 @@
 /**
  * Breaks down OCCLUDED->DOZING transition into discrete steps for corresponding views to consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class OccludedToDozingTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
index 47e202b..e74607d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
@@ -17,12 +17,14 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
 import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -32,16 +34,20 @@
 @Inject
 constructor(
     animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+    private val blurFactory: GlanceableHubBlurComponent.Factory,
+) : DeviceEntryIconTransition, GlanceableHubTransition {
 
     private val transitionAnimation =
         animationFlow
             .setup(
                 duration = TO_GLANCEABLE_HUB_DURATION,
-                edge = Edge.create(OCCLUDED, Scenes.Communal)
+                edge = Edge.create(OCCLUDED, Scenes.Communal),
             )
             .setupWithoutSceneContainer(edge = Edge.create(OCCLUDED, GLANCEABLE_HUB))
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(1f)
+
+    override val windowBlurRadius: Flow<Float> =
+        blurFactory.create(transitionAnimation).getBlurProvider().enterBlurRadius
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
index 98dba39..88d6638 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
@@ -24,11 +24,9 @@
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /** Breaks down OCCLUDED->GONE transition into discrete steps for corresponding views to consume. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class OccludedToGoneTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index d10970f..6b9d4cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
@@ -44,7 +43,6 @@
  * Breaks down OCCLUDED->LOCKSCREEN transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class OccludedToLockscreenTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
index 4d3e272..3c126aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -26,12 +27,16 @@
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
 
 @SysUISingleton
 class OccludedToPrimaryBouncerTransitionViewModel
 @Inject
-constructor(blurConfig: BlurConfig, animationFlow: KeyguardTransitionAnimationFlow) :
-    PrimaryBouncerTransition {
+constructor(
+    shadeDependentFlows: ShadeDependentFlows,
+    blurConfig: BlurConfig,
+    animationFlow: KeyguardTransitionAnimationFlow,
+) : PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
@@ -41,8 +46,21 @@
             .setupWithoutSceneContainer(edge = Edge.create(OCCLUDED, PRIMARY_BOUNCER))
 
     override val windowBlurRadius: Flow<Float> =
-        transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+        shadeDependentFlows.transitionFlow(
+            flowWhenShadeIsExpanded =
+                if (Flags.notificationShadeBlur()) {
+                    transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+                } else {
+                    emptyFlow()
+                },
+            flowWhenShadeIsNotExpanded =
+                transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
+        )
 
     override val notificationBlurRadius: Flow<Float> =
-        transitionAnimation.immediatelyTransitionTo(0.0f)
+        shadeDependentFlows.transitionFlow(
+            flowWhenShadeIsExpanded =
+                transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
+            flowWhenShadeIsNotExpanded = emptyFlow(),
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludingAppDeviceEntryMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludingAppDeviceEntryMessageViewModel.kt
index 846bcbf..d32b3b4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludingAppDeviceEntryMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludingAppDeviceEntryMessageViewModel.kt
@@ -21,11 +21,9 @@
 import com.android.systemui.deviceentry.domain.interactor.OccludingAppDeviceEntryInteractor
 import com.android.systemui.deviceentry.shared.model.BiometricMessage
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /** Shows authentication messages over occcluding apps over the lockscreen. */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class OccludingAppDeviceEntryMessageViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
index 224191b..dc75829 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -38,7 +37,6 @@
  * Breaks down PRIMARY BOUNCER->AOD transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class PrimaryBouncerToAodTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
index 0f8495f..7f431bc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -37,7 +36,6 @@
  * Breaks down PRIMARY BOUNCER->DOZING transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class PrimaryBouncerToDozingTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
index a13eef2..89c0ba1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.Flags
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
 import com.android.systemui.keyguard.shared.model.Edge
@@ -27,12 +30,17 @@
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
 
 @SysUISingleton
 class PrimaryBouncerToGlanceableHubTransitionViewModel
 @Inject
-constructor(private val blurConfig: BlurConfig, animationFlow: KeyguardTransitionAnimationFlow) :
-    DeviceEntryIconTransition, PrimaryBouncerTransition {
+constructor(
+    blurConfig: BlurConfig,
+    animationFlow: KeyguardTransitionAnimationFlow,
+    communalSettingsInteractor: CommunalSettingsInteractor,
+) : DeviceEntryIconTransition, PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
             .setup(duration = TO_GLANCEABLE_HUB_DURATION, edge = Edge.INVALID)
@@ -42,7 +50,15 @@
         transitionAnimation.immediatelyTransitionTo(1f)
 
     override val windowBlurRadius: Flow<Float> =
-        transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx)
+        if (Flags.glanceableHubBlurredBackground()) {
+            communalSettingsInteractor.communalBackground
+                .filter { it != CommunalBackgroundType.BLUR }
+                .flatMapLatest {
+                    transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx)
+                }
+        } else {
+            transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx)
+        }
 
     override val notificationBlurRadius: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0.0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index d1233f2..34c87fe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -33,7 +33,6 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 
@@ -41,7 +40,6 @@
  * Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to
  * consume.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class PrimaryBouncerToGoneTransitionViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index c53a408..9cb0387 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -18,6 +18,7 @@
 
 import android.util.MathUtils
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -30,14 +31,13 @@
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
 
 /**
  * Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to
  * consume.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class PrimaryBouncerToLockscreenTransitionViewModel
 @Inject
@@ -78,7 +78,11 @@
     override val windowBlurRadius: Flow<Float> =
         shadeDependentFlows.transitionFlow(
             flowWhenShadeIsExpanded =
-                transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
+                if (Flags.notificationShadeBlur()) {
+                    transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+                } else {
+                    emptyFlow()
+                },
             flowWhenShadeIsNotExpanded =
                 transitionAnimation.sharedFlow(
                     duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt
index fe1708e..0f0e7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -26,12 +27,16 @@
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
 
 @SysUISingleton
 class PrimaryBouncerToOccludedTransitionViewModel
 @Inject
-constructor(private val blurConfig: BlurConfig, animationFlow: KeyguardTransitionAnimationFlow) :
-    PrimaryBouncerTransition {
+constructor(
+    shadeDependentFlows: ShadeDependentFlows,
+    blurConfig: BlurConfig,
+    animationFlow: KeyguardTransitionAnimationFlow,
+) : PrimaryBouncerTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
@@ -41,7 +46,16 @@
             .setupWithoutSceneContainer(edge = Edge.create(PRIMARY_BOUNCER, OCCLUDED))
 
     override val windowBlurRadius: Flow<Float> =
-        transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx)
+        shadeDependentFlows.transitionFlow(
+            flowWhenShadeIsExpanded =
+                if (Flags.notificationShadeBlur()) {
+                    transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+                } else {
+                    emptyFlow()
+                },
+            flowWhenShadeIsNotExpanded =
+                transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx),
+        )
 
     override val notificationBlurRadius: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0.0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
index 3de1f1e..106ecc6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -45,7 +45,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -62,7 +61,6 @@
 import kotlinx.coroutines.flow.onCompletion
 import com.android.app.tracing.coroutines.launchTraced as launch
 
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class SideFpsProgressBarViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardClockLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardClockLog.kt
index 8732ef5..5097968 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardClockLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardClockLog.kt
@@ -24,6 +24,12 @@
 @Retention(AnnotationRetention.RUNTIME)
 annotation class KeyguardClockLog
 
+/** A [com.android.systemui.log.LogBuffer] for keyguard blueprint logs. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class KeyguardBlueprintLog
+
 /** A [com.android.systemui.log.LogBuffer] for small keyguard clock logs. */
 @Qualifier
 @MustBeDocumented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 8097d95..faa6c52 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -321,6 +321,16 @@
     }
 
     /**
+     * Provides a {@link LogBuffer} for keyguard blueprint logs.
+     */
+    @Provides
+    @SysUISingleton
+    @KeyguardBlueprintLog
+    public static LogBuffer provideKeyguardBlueprintLog(LogBufferFactory factory) {
+        return factory.create("KeyguardBlueprintLog", 100);
+    }
+
+    /**
      * Provides a {@link LogBuffer} for general keyguard clock logs.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerDebug.kt b/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerDebug.kt
index 21808b6..704c990 100644
--- a/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerDebug.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerDebug.kt
@@ -26,7 +26,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
@@ -39,7 +38,6 @@
  * Note that some log messages may fail to be echoed while the systemui process is first starting
  * up, before we load the echo settings.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 class LogcatEchoTrackerDebug
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
index baa07c1..9fddbfb 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
@@ -65,7 +65,7 @@
  */
 fun <T : Diffable<T>> Flow<T>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
-    columnPrefix: String,
+    columnPrefix: String = "",
     initialValue: T,
 ): Flow<T> {
     // Fully log the initial value to the table.
@@ -87,7 +87,7 @@
 /** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun Flow<Boolean>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
-    columnPrefix: String,
+    columnPrefix: String = "",
     columnName: String,
     initialValue: Boolean,
 ): Flow<Boolean> {
@@ -106,7 +106,7 @@
 /** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun Flow<Int>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
-    columnPrefix: String,
+    columnPrefix: String = "",
     columnName: String,
     initialValue: Int,
 ): Flow<Int> {
@@ -125,7 +125,7 @@
 /** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun Flow<Int?>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
-    columnPrefix: String,
+    columnPrefix: String = "",
     columnName: String,
     initialValue: Int?,
 ): Flow<Int?> {
@@ -144,7 +144,7 @@
 /** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun Flow<String?>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
-    columnPrefix: String,
+    columnPrefix: String = "",
     columnName: String,
     initialValue: String?,
 ): Flow<String?> {
@@ -163,7 +163,7 @@
 /** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun <T> Flow<List<T>>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
-    columnPrefix: String,
+    columnPrefix: String = "",
     columnName: String,
     initialValue: List<T>,
 ): Flow<List<T>> {
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 89a599a..3d1623b 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -124,7 +124,7 @@
      *   the separator token for parsing, so it can't be present in any part of the column name.
      */
     @Synchronized
-    fun <T : Diffable<T>> logDiffs(columnPrefix: String, prevVal: T, newVal: T) {
+    fun <T : Diffable<T>> logDiffs(columnPrefix: String = "", prevVal: T, newVal: T) {
         val row = tempRow
         row.timestamp = systemClock.currentTimeMillis()
         row.columnPrefix = columnPrefix
@@ -136,6 +136,7 @@
     /**
      * Logs change(s) to the buffer using [rowInitializer].
      *
+     * @param columnPrefix see [logDiffs].
      * @param rowInitializer a function that will be called immediately to store relevant data on
      *   the row.
      * @param isInitial true if this change represents the starting value for a particular column
@@ -145,9 +146,9 @@
      */
     @Synchronized
     fun logChange(
-        columnPrefix: String,
+        columnPrefix: String = "",
         isInitial: Boolean = false,
-        rowInitializer: (TableRowLogger) -> Unit
+        rowInitializer: (TableRowLogger) -> Unit,
     ) {
         val row = tempRow
         row.timestamp = systemClock.currentTimeMillis()
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
index 425e674e..fb69b79 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize
 import com.android.systemui.log.LogcatEchoTracker
 import com.android.systemui.util.time.SystemClock
+import java.util.concurrent.ConcurrentHashMap
 import javax.inject.Inject
 
 @SysUISingleton
@@ -31,7 +32,7 @@
     private val systemClock: SystemClock,
     private val logcatEchoTracker: LogcatEchoTracker,
 ) {
-    private val existingBuffers = mutableMapOf<String, TableLogBuffer>()
+    private val existingBuffers = ConcurrentHashMap<String, TableLogBuffer>()
 
     /**
      * Creates a new [TableLogBuffer]. This method should only be called from static contexts, where
@@ -42,17 +43,9 @@
      * @param maxSize the buffer max size. See [adjustMaxSize]
      * @return a new [TableLogBuffer] registered with [DumpManager]
      */
-    fun create(
-        name: String,
-        maxSize: Int,
-    ): TableLogBuffer {
+    fun create(name: String, maxSize: Int): TableLogBuffer {
         val tableBuffer =
-            TableLogBuffer(
-                adjustMaxSize(maxSize),
-                name,
-                systemClock,
-                logcatEchoTracker,
-            )
+            TableLogBuffer(adjustMaxSize(maxSize), name, systemClock, logcatEchoTracker)
         dumpManager.registerTableLogBuffer(name, tableBuffer)
         return tableBuffer
     }
@@ -66,13 +59,12 @@
      *
      * @return a [TableLogBuffer] suitable for reuse
      */
-    fun getOrCreate(
-        name: String,
-        maxSize: Int,
-    ): TableLogBuffer =
-        existingBuffers.getOrElse(name) {
-            val buffer = create(name, maxSize)
-            existingBuffers[name] = buffer
-            buffer
+    fun getOrCreate(name: String, maxSize: Int): TableLogBuffer =
+        synchronized(existingBuffers) {
+            existingBuffers.getOrElse(name) {
+                val buffer = create(name, maxSize)
+                existingBuffers[name] = buffer
+                buffer
+            }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt
new file mode 100644
index 0000000..ece97bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.lowlightclock
+
+import android.annotation.IntDef
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import android.util.Log
+import com.android.systemui.Dumpable
+import com.android.systemui.lowlightclock.dagger.LowLightModule.LIGHT_SENSOR
+import com.android.systemui.util.sensors.AsyncSensorManager
+import java.io.PrintWriter
+import java.util.Optional
+import javax.inject.Inject
+import javax.inject.Named
+
+/**
+ * Monitors ambient light signals, applies a debouncing algorithm, and produces the current ambient
+ * light mode.
+ *
+ * @property algorithm the debounce algorithm which transforms light sensor events into an ambient
+ *   light mode.
+ * @property sensorManager the sensor manager used to register sensor event updates.
+ */
+class AmbientLightModeMonitor
+@Inject
+constructor(
+    private val algorithm: Optional<DebounceAlgorithm>,
+    private val sensorManager: AsyncSensorManager,
+    @Named(LIGHT_SENSOR) private val lightSensor: Optional<Sensor>,
+) : Dumpable {
+    companion object {
+        private const val TAG = "AmbientLightModeMonitor"
+        private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+
+        const val AMBIENT_LIGHT_MODE_LIGHT = 0
+        const val AMBIENT_LIGHT_MODE_DARK = 1
+        const val AMBIENT_LIGHT_MODE_UNDECIDED = 2
+    }
+
+    // Represents all ambient light modes.
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(AMBIENT_LIGHT_MODE_LIGHT, AMBIENT_LIGHT_MODE_DARK, AMBIENT_LIGHT_MODE_UNDECIDED)
+    annotation class AmbientLightMode
+
+    /**
+     * Start monitoring the current ambient light mode.
+     *
+     * @param callback callback that gets triggered when the ambient light mode changes.
+     */
+    fun start(callback: Callback) {
+        if (DEBUG) Log.d(TAG, "start monitoring ambient light mode")
+
+        if (lightSensor.isEmpty) {
+            if (DEBUG) Log.w(TAG, "light sensor not available")
+            return
+        }
+
+        if (algorithm.isEmpty) {
+            if (DEBUG) Log.w(TAG, "debounce algorithm not available")
+            return
+        }
+
+        algorithm.get().start(callback)
+        sensorManager.registerListener(
+            mSensorEventListener,
+            lightSensor.get(),
+            SensorManager.SENSOR_DELAY_NORMAL,
+        )
+    }
+
+    /** Stop monitoring the current ambient light mode. */
+    fun stop() {
+        if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode")
+
+        if (algorithm.isPresent) {
+            algorithm.get().stop()
+        }
+        sensorManager.unregisterListener(mSensorEventListener)
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println()
+        pw.println("Ambient light mode monitor:")
+        pw.println("  lightSensor=$lightSensor")
+        pw.println()
+    }
+
+    private val mSensorEventListener: SensorEventListener =
+        object : SensorEventListener {
+            override fun onSensorChanged(event: SensorEvent) {
+                if (event.values.isEmpty()) {
+                    if (DEBUG) Log.w(TAG, "SensorEvent doesn't have any value")
+                    return
+                }
+
+                if (algorithm.isPresent) {
+                    algorithm.get().onUpdateLightSensorEvent(event.values[0])
+                }
+            }
+
+            override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
+                // Do nothing.
+            }
+        }
+
+    /** Interface of the ambient light mode callback, which gets triggered when the mode changes. */
+    interface Callback {
+        fun onChange(@AmbientLightMode mode: Int)
+    }
+
+    /** Interface of the algorithm that transforms light sensor events to an ambient light mode. */
+    interface DebounceAlgorithm {
+        // Setting Callback to nullable so mockito can verify without throwing NullPointerException.
+        fun start(callback: Callback?)
+
+        fun stop()
+
+        fun onUpdateLightSensorEvent(value: Float)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ChargingStatusProvider.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ChargingStatusProvider.java
new file mode 100644
index 0000000..8cc399b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ChargingStatusProvider.java
@@ -0,0 +1,258 @@
+/*
+ * 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.lowlightclock;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.BatteryManager;
+import android.os.RemoteException;
+import android.text.format.Formatter;
+import android.util.Log;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.Preconditions;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
+
+import java.text.NumberFormat;
+
+import javax.inject.Inject;
+
+/**
+ * Provides charging status as a string to a registered callback such that it can be displayed to
+ * the user (e.g. on the low-light clock).
+ * TODO(b/223681352): Make this code shareable with {@link KeyguardIndicationController}.
+ */
+public class ChargingStatusProvider {
+    private static final String TAG = "ChargingStatusProvider";
+
+    private final Resources mResources;
+    private final Context mContext;
+    private final IBatteryStats mBatteryInfo;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final BatteryState mBatteryState = new BatteryState();
+    // This callback is registered with KeyguardUpdateMonitor, which only keeps weak references to
+    // its callbacks. Therefore, an explicit reference needs to be kept here to avoid the
+    // callback being GC'd.
+    private ChargingStatusCallback mChargingStatusCallback;
+
+    private Callback mCallback;
+
+    @Inject
+    public ChargingStatusProvider(
+            Context context,
+            @Main Resources resources,
+            IBatteryStats iBatteryStats,
+            KeyguardUpdateMonitor keyguardUpdateMonitor) {
+        mContext = context;
+        mResources = resources;
+        mBatteryInfo = iBatteryStats;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+    }
+
+    /**
+     * Start using the {@link ChargingStatusProvider}.
+     * @param callback A callback to be called when the charging status changes.
+     */
+    public void startUsing(Callback callback) {
+        Preconditions.checkState(
+                mCallback == null, "ChargingStatusProvider already started!");
+        mCallback = callback;
+        mChargingStatusCallback = new ChargingStatusCallback();
+        mKeyguardUpdateMonitor.registerCallback(mChargingStatusCallback);
+        reportStatusToCallback();
+    }
+
+    /**
+     * Stop using the {@link ChargingStatusProvider}.
+     */
+    public void stopUsing() {
+        mCallback = null;
+
+        if (mChargingStatusCallback != null) {
+            mKeyguardUpdateMonitor.removeCallback(mChargingStatusCallback);
+            mChargingStatusCallback = null;
+        }
+    }
+
+    private String computeChargingString() {
+        if (!mBatteryState.isValid()) {
+            return null;
+        }
+
+        int chargingId;
+
+        if (mBatteryState.isBatteryDefender()) {
+            return mResources.getString(
+                    R.string.keyguard_plugged_in_charging_limited,
+                    mBatteryState.getBatteryLevelAsPercentage());
+        } else if (mBatteryState.isPowerCharged()) {
+            return mResources.getString(R.string.keyguard_charged);
+        }
+
+        final long chargingTimeRemaining = mBatteryState.getChargingTimeRemaining(mBatteryInfo);
+        final boolean hasChargingTime = chargingTimeRemaining > 0;
+        if (mBatteryState.isPowerPluggedInWired()) {
+            switch (mBatteryState.getChargingSpeed(mContext)) {
+                case BatteryStatus.CHARGING_FAST:
+                    chargingId = hasChargingTime
+                            ? R.string.keyguard_indication_charging_time_fast
+                            : R.string.keyguard_plugged_in_charging_fast;
+                    break;
+                case BatteryStatus.CHARGING_SLOWLY:
+                    chargingId = hasChargingTime
+                            ? R.string.keyguard_indication_charging_time_slowly
+                            : R.string.keyguard_plugged_in_charging_slowly;
+                    break;
+                default:
+                    chargingId = hasChargingTime
+                            ? R.string.keyguard_indication_charging_time
+                            : R.string.keyguard_plugged_in;
+                    break;
+            }
+        } else if (mBatteryState.isPowerPluggedInWireless()) {
+            chargingId = hasChargingTime
+                    ? R.string.keyguard_indication_charging_time_wireless
+                    : R.string.keyguard_plugged_in_wireless;
+        } else if (mBatteryState.isPowerPluggedInDocked()) {
+            chargingId = hasChargingTime
+                    ? R.string.keyguard_indication_charging_time_dock
+                    : R.string.keyguard_plugged_in_dock;
+        } else {
+            chargingId = hasChargingTime
+                    ? R.string.keyguard_indication_charging_time
+                    : R.string.keyguard_plugged_in;
+        }
+
+        final String percentage = mBatteryState.getBatteryLevelAsPercentage();
+        if (hasChargingTime) {
+            final String chargingTimeFormatted =
+                    Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+                            mContext, chargingTimeRemaining);
+            return mResources.getString(chargingId, chargingTimeFormatted,
+                    percentage);
+        } else {
+            return mResources.getString(chargingId, percentage);
+        }
+    }
+
+    private void reportStatusToCallback() {
+        if (mCallback != null) {
+            final boolean shouldShowStatus =
+                    mBatteryState.isPowerPluggedIn() || mBatteryState.isBatteryDefenderEnabled();
+            mCallback.onChargingStatusChanged(shouldShowStatus, computeChargingString());
+        }
+    }
+
+    private class ChargingStatusCallback extends KeyguardUpdateMonitorCallback {
+        @Override
+        public void onRefreshBatteryInfo(BatteryStatus status) {
+            mBatteryState.setBatteryStatus(status);
+            reportStatusToCallback();
+        }
+    }
+
+    /***
+     * A callback to be called when the charging status changes.
+     */
+    public interface Callback {
+        /***
+         * Called when the charging status changes.
+         * @param shouldShowStatus Whether or not to show a charging status message.
+         * @param statusMessage A charging status message.
+         */
+        void onChargingStatusChanged(boolean shouldShowStatus, String statusMessage);
+    }
+
+    /***
+     * A wrapper around {@link BatteryStatus} for fetching various properties of the current
+     * battery and charging state.
+     */
+    private static class BatteryState {
+        private BatteryStatus mBatteryStatus;
+
+        public void setBatteryStatus(BatteryStatus batteryStatus) {
+            mBatteryStatus = batteryStatus;
+        }
+
+        public boolean isValid() {
+            return mBatteryStatus != null;
+        }
+
+        public long getChargingTimeRemaining(IBatteryStats batteryInfo) {
+            try {
+                return isPowerPluggedIn() ? batteryInfo.computeChargeTimeRemaining() : -1;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error calling IBatteryStats: ", e);
+                return -1;
+            }
+        }
+
+        public boolean isBatteryDefenderEnabled() {
+            return isValid() && mBatteryStatus.isPluggedIn() && isBatteryDefender();
+        }
+
+        public boolean isBatteryDefender() {
+            return isValid() && mBatteryStatus.isBatteryDefender();
+        }
+
+        public int getBatteryLevel() {
+            return isValid() ? mBatteryStatus.level : 0;
+        }
+
+        public int getChargingSpeed(Context context) {
+            return isValid() ? mBatteryStatus.getChargingSpeed(context) : 0;
+        }
+
+        public boolean isPowerCharged() {
+            return isValid() && mBatteryStatus.isCharged();
+        }
+
+        public boolean isPowerPluggedIn() {
+            return isValid() && mBatteryStatus.isPluggedIn() && isChargingOrFull();
+        }
+
+        public boolean isPowerPluggedInWired() {
+            return isValid()
+                    && mBatteryStatus.isPluggedInWired()
+                    && isChargingOrFull();
+        }
+
+        public boolean isPowerPluggedInWireless() {
+            return isValid()
+                    && mBatteryStatus.isPluggedInWireless()
+                    && isChargingOrFull();
+        }
+
+        public boolean isPowerPluggedInDocked() {
+            return isValid() && mBatteryStatus.isPluggedInDock() && isChargingOrFull();
+        }
+
+        private boolean isChargingOrFull() {
+            return isValid()
+                    && (mBatteryStatus.status == BatteryManager.BATTERY_STATUS_CHARGING
+                        || mBatteryStatus.isCharged());
+        }
+
+        private String getBatteryLevelAsPercentage() {
+            return NumberFormat.getPercentInstance().format(getBatteryLevel() / 100f);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
new file mode 100644
index 0000000..4c1da01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.lowlightclock
+
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserManager
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.shared.condition.Condition
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.cancellable
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+class DirectBootCondition
+@Inject
+constructor(
+    broadcastDispatcher: BroadcastDispatcher,
+    private val userManager: UserManager,
+    @Application private val coroutineScope: CoroutineScope,
+) : Condition(coroutineScope) {
+    private var job: Job? = null
+    private val directBootFlow =
+        broadcastDispatcher
+            .broadcastFlow(IntentFilter(Intent.ACTION_USER_UNLOCKED))
+            .map { !userManager.isUserUnlocked }
+            .cancellable()
+            .distinctUntilChanged()
+
+    override fun start() {
+        job = coroutineScope.launch { directBootFlow.collect { updateCondition(it) } }
+        updateCondition(!userManager.isUserUnlocked)
+    }
+
+    override fun stop() {
+        job?.cancel()
+    }
+
+    override fun getStartStrategy(): Int {
+        return START_EAGERLY
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java
new file mode 100644
index 0000000..7f21d07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java
@@ -0,0 +1,137 @@
+/*
+ * 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.lowlightclock;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.statusbar.commandline.Command;
+import com.android.systemui.statusbar.commandline.CommandRegistry;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * This condition registers for and fulfills cmd shell commands to force a device into or out of
+ * low-light conditions.
+ */
+public class ForceLowLightCondition extends Condition {
+    /**
+     * Command root
+     */
+    public static final String COMMAND_ROOT = "low-light";
+    /**
+     * Command for forcing device into low light.
+     */
+    public static final String COMMAND_ENABLE_LOW_LIGHT = "enable";
+
+    /**
+     * Command for preventing a device from entering low light.
+     */
+    public static final String COMMAND_DISABLE_LOW_LIGHT = "disable";
+
+    /**
+     * Command for clearing previously forced low-light conditions.
+     */
+    public static final String COMMAND_CLEAR_LOW_LIGHT = "clear";
+
+    private static final String TAG = "ForceLowLightCondition";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    /**
+     * Default Constructor.
+     *
+     * @param commandRegistry command registry to register commands with.
+     */
+    @Inject
+    public ForceLowLightCondition(
+            @Application CoroutineScope scope,
+            CommandRegistry commandRegistry
+    ) {
+        super(scope, null, true);
+
+        if (DEBUG) {
+            Log.d(TAG, "registering commands");
+        }
+        commandRegistry.registerCommand(COMMAND_ROOT, () -> new Command() {
+            @Override
+            public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) {
+                if (args.size() != 1) {
+                    pw.println("no command specified");
+                    help(pw);
+                    return;
+                }
+
+                final String cmd = args.get(0);
+
+                if (TextUtils.equals(cmd, COMMAND_ENABLE_LOW_LIGHT)) {
+                    logAndPrint(pw, "forcing low light");
+                    updateCondition(true);
+                } else if (TextUtils.equals(cmd, COMMAND_DISABLE_LOW_LIGHT)) {
+                    logAndPrint(pw, "forcing to not enter low light");
+                    updateCondition(false);
+                } else if (TextUtils.equals(cmd, COMMAND_CLEAR_LOW_LIGHT)) {
+                    logAndPrint(pw, "clearing any forced low light");
+                    clearCondition();
+                } else {
+                    pw.println("invalid command");
+                    help(pw);
+                }
+            }
+
+            @Override
+            public void help(@NonNull PrintWriter pw) {
+                pw.println("Usage: adb shell cmd statusbar low-light <cmd>");
+                pw.println("Supported commands:");
+                pw.println("  - enable");
+                pw.println("    forces device into low-light");
+                pw.println("  - disable");
+                pw.println("    forces device to not enter low-light");
+                pw.println("  - clear");
+                pw.println("    clears any previously forced state");
+            }
+
+            private void logAndPrint(PrintWriter pw, String message) {
+                pw.println(message);
+                if (DEBUG) {
+                    Log.d(TAG, message);
+                }
+            }
+        });
+    }
+
+    @Override
+    protected void start() {
+    }
+
+    @Override
+    protected void stop() {
+    }
+
+    @Override
+    protected int getStartStrategy() {
+        return START_EAGERLY;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockAnimationProvider.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockAnimationProvider.java
new file mode 100644
index 0000000..6de5998
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockAnimationProvider.java
@@ -0,0 +1,127 @@
+/*
+ * 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.lowlightclock;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.app.animation.Interpolators;
+import com.android.dream.lowlight.util.TruncatedInterpolator;
+import com.android.systemui.lowlightclock.dagger.LowLightModule;
+import com.android.systemui.statusbar.CrossFadeHelper;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/***
+ * A class that provides the animations used by the low-light clock.
+ *
+ * The entry and exit animations are opposites, with the only difference being a delay before the
+ * text fades in on entry.
+ */
+public class LowLightClockAnimationProvider {
+    private final int mYTranslationAnimationInStartOffset;
+    private final long mYTranslationAnimationInDurationMillis;
+    private final long mAlphaAnimationInStartDelayMillis;
+    private final long mAlphaAnimationDurationMillis;
+
+    /**
+     * Custom interpolator used for the translate out animation, which uses an emphasized easing
+     * like the translate in animation, but is scaled to match the length of the alpha animation.
+     */
+    private final Interpolator mTranslationOutInterpolator;
+
+    @Inject
+    public LowLightClockAnimationProvider(
+            @Named(LowLightModule.Y_TRANSLATION_ANIMATION_OFFSET)
+                    int yTranslationAnimationInStartOffset,
+            @Named(LowLightModule.Y_TRANSLATION_ANIMATION_DURATION_MILLIS)
+                    long yTranslationAnimationInDurationMillis,
+            @Named(LowLightModule.ALPHA_ANIMATION_IN_START_DELAY_MILLIS)
+                    long alphaAnimationInStartDelayMillis,
+            @Named(LowLightModule.ALPHA_ANIMATION_DURATION_MILLIS)
+                    long alphaAnimationDurationMillis) {
+        mYTranslationAnimationInStartOffset = yTranslationAnimationInStartOffset;
+        mYTranslationAnimationInDurationMillis = yTranslationAnimationInDurationMillis;
+        mAlphaAnimationInStartDelayMillis = alphaAnimationInStartDelayMillis;
+        mAlphaAnimationDurationMillis = alphaAnimationDurationMillis;
+
+        mTranslationOutInterpolator = new TruncatedInterpolator(Interpolators.EMPHASIZED,
+                /*originalDuration=*/ mYTranslationAnimationInDurationMillis,
+                /*newDuration=*/ mAlphaAnimationDurationMillis);
+    }
+
+    /***
+     * Provides an animation for when the given views become visible.
+     * @param views Any number of views to animate in together.
+     */
+    public Animator provideAnimationIn(View... views) {
+        final AnimatorSet animatorSet = new AnimatorSet();
+
+        for (View view : views) {
+            if (view == null) continue;
+            // Set the alpha to 0 to start because the alpha animation has a start delay.
+            CrossFadeHelper.fadeOut(view, 0f, false);
+
+            final Animator alphaAnimator =
+                    ObjectAnimator.ofFloat(view, View.ALPHA, 1f);
+            alphaAnimator.setStartDelay(mAlphaAnimationInStartDelayMillis);
+            alphaAnimator.setDuration(mAlphaAnimationDurationMillis);
+            alphaAnimator.setInterpolator(Interpolators.LINEAR);
+
+            final Animator positionAnimator = ObjectAnimator
+                    .ofFloat(view, View.TRANSLATION_Y, mYTranslationAnimationInStartOffset, 0f);
+            positionAnimator.setDuration(mYTranslationAnimationInDurationMillis);
+            positionAnimator.setInterpolator(Interpolators.EMPHASIZED);
+
+            // The position animator must be started first since the alpha animator has a start
+            // delay.
+            animatorSet.playTogether(positionAnimator, alphaAnimator);
+        }
+
+        return animatorSet;
+    }
+
+    /***
+     * Provides an animation for when the given views are going out of view.
+     * @param views Any number of views to animate out.
+     */
+    public Animator provideAnimationOut(View... views) {
+        final AnimatorSet animatorSet = new AnimatorSet();
+
+        for (View view : views) {
+            if (view == null) continue;
+            final Animator alphaAnimator =
+                    ObjectAnimator.ofFloat(view, View.ALPHA, 0f);
+            alphaAnimator.setDuration(mAlphaAnimationDurationMillis);
+            alphaAnimator.setInterpolator(Interpolators.LINEAR);
+
+            final Animator positionAnimator = ObjectAnimator
+                    .ofFloat(view, View.TRANSLATION_Y, mYTranslationAnimationInStartOffset);
+            // Use the same duration as the alpha animation plus our custom interpolator.
+            positionAnimator.setDuration(mAlphaAnimationDurationMillis);
+            positionAnimator.setInterpolator(mTranslationOutInterpolator);
+            animatorSet.playTogether(alphaAnimator, positionAnimator);
+        }
+
+        return animatorSet;
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java
new file mode 100644
index 0000000..e91be50
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java
@@ -0,0 +1,77 @@
+/*
+ * 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.lowlightclock;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.shared.condition.Condition;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import javax.inject.Inject;
+
+/**
+ * Condition for monitoring when the device enters and exits lowlight mode.
+ */
+public class LowLightCondition extends Condition {
+    private final AmbientLightModeMonitor mAmbientLightModeMonitor;
+    private final UiEventLogger mUiEventLogger;
+
+    @Inject
+    public LowLightCondition(@Application CoroutineScope scope,
+            AmbientLightModeMonitor ambientLightModeMonitor,
+            UiEventLogger uiEventLogger) {
+        super(scope);
+        mAmbientLightModeMonitor = ambientLightModeMonitor;
+        mUiEventLogger = uiEventLogger;
+    }
+
+    @Override
+    protected void start() {
+        mAmbientLightModeMonitor.start(this::onLowLightChanged);
+    }
+
+    @Override
+    protected void stop() {
+        mAmbientLightModeMonitor.stop();
+
+        // Reset condition met to false.
+        updateCondition(false);
+    }
+
+    @Override
+    protected int getStartStrategy() {
+        // As this condition keeps the lowlight sensor active, it should only run when needed.
+        return START_WHEN_NEEDED;
+    }
+
+    private void onLowLightChanged(int lowLightMode) {
+        if (lowLightMode == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED) {
+            // Ignore undecided mode changes.
+            return;
+        }
+
+        final boolean isLowLight = lowLightMode == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK;
+        if (isLowLight == isConditionMet()) {
+            // No change in condition, don't do anything.
+            return;
+        }
+        mUiEventLogger.log(isLowLight ? LowLightDockEvent.AMBIENT_LIGHT_TO_DARK
+                : LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT);
+        updateCondition(isLowLight);
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDisplayController.kt
similarity index 61%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt
rename to packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDisplayController.kt
index 6345c40..9a9d813 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDisplayController.kt
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.shade.ui.viewmodel
+package com.android.systemui.lowlightclock
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeUserActionsViewModel
+interface LowLightDisplayController {
+    fun isDisplayBrightnessModeSupported(): Boolean
 
-val Kosmos.notificationsShadeUserActionsViewModel:
-    NotificationsShadeUserActionsViewModel by Fixture { NotificationsShadeUserActionsViewModel() }
+    fun setDisplayBrightnessModeEnabled(enabled: Boolean)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDockEvent.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDockEvent.kt
new file mode 100644
index 0000000..b99aeb6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDockEvent.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.lowlightclock
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class LowLightDockEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+    @UiEvent(doc = "Ambient light changed from light to dark") AMBIENT_LIGHT_TO_DARK(999),
+    @UiEvent(doc = "The low light mode has started") LOW_LIGHT_STARTED(1000),
+    @UiEvent(doc = "Ambient light changed from dark to light") AMBIENT_LIGHT_TO_LIGHT(1001),
+    @UiEvent(doc = "The low light mode has stopped") LOW_LIGHT_STOPPED(1002);
+
+    override fun getId(): Int {
+        return id
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightLogger.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightLogger.kt
new file mode 100644
index 0000000..11d7521
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightLogger.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.lowlightclock
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.lowlightclock.dagger.LowLightLog
+import javax.inject.Inject
+
+/** Logs to a {@link LogBuffer} anything related to low-light features. */
+class LowLightLogger @Inject constructor(@LowLightLog private val buffer: LogBuffer) {
+    /** Logs a debug message to the buffer. */
+    fun d(tag: String, message: String) = buffer.log(tag, LogLevel.DEBUG, message)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.java
new file mode 100644
index 0000000..912ace7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.java
@@ -0,0 +1,133 @@
+/*
+ * 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.lowlightclock;
+
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT;
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR;
+import static com.android.systemui.dreams.dagger.DreamModule.LOW_LIGHT_DREAM_SERVICE;
+import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
+import static com.android.systemui.lowlightclock.dagger.LowLightModule.LOW_LIGHT_PRECONDITIONS;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+
+import androidx.annotation.Nullable;
+
+import com.android.dream.lowlight.LowLightDreamManager;
+import com.android.systemui.dagger.qualifiers.SystemUser;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
+
+import dagger.Lazy;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Tracks environment (low-light or not) in order to correctly show or hide a low-light clock while
+ * dreaming.
+ */
+public class LowLightMonitor extends ConditionalCoreStartable implements Monitor.Callback,
+        ScreenLifecycle.Observer {
+    private static final String TAG = "LowLightMonitor";
+
+    private final Lazy<LowLightDreamManager> mLowLightDreamManager;
+    private final Monitor mConditionsMonitor;
+    private final Lazy<Set<Condition>> mLowLightConditions;
+    private Monitor.Subscription.Token mSubscriptionToken;
+    private ScreenLifecycle mScreenLifecycle;
+    private final LowLightLogger mLogger;
+
+    private final ComponentName mLowLightDreamService;
+
+    private final PackageManager mPackageManager;
+
+    @Inject
+    public LowLightMonitor(Lazy<LowLightDreamManager> lowLightDreamManager,
+            @SystemUser Monitor conditionsMonitor,
+            @Named(LOW_LIGHT_PRECONDITIONS) Lazy<Set<Condition>> lowLightConditions,
+            ScreenLifecycle screenLifecycle,
+            LowLightLogger lowLightLogger,
+            @Nullable @Named(LOW_LIGHT_DREAM_SERVICE) ComponentName lowLightDreamService,
+            PackageManager packageManager) {
+        super(conditionsMonitor);
+        mLowLightDreamManager = lowLightDreamManager;
+        mConditionsMonitor = conditionsMonitor;
+        mLowLightConditions = lowLightConditions;
+        mScreenLifecycle = screenLifecycle;
+        mLogger = lowLightLogger;
+        mLowLightDreamService = lowLightDreamService;
+        mPackageManager = packageManager;
+    }
+
+    @Override
+    public void onConditionsChanged(boolean allConditionsMet) {
+        mLogger.d(TAG, "Low light enabled: " + allConditionsMet);
+
+        mLowLightDreamManager.get().setAmbientLightMode(allConditionsMet
+                ? AMBIENT_LIGHT_MODE_LOW_LIGHT : AMBIENT_LIGHT_MODE_REGULAR);
+    }
+
+    @Override
+    public void onScreenTurnedOn() {
+        if (mSubscriptionToken == null) {
+            mLogger.d(TAG, "Screen turned on. Subscribing to low light conditions.");
+
+            mSubscriptionToken = mConditionsMonitor.addSubscription(
+                new Monitor.Subscription.Builder(this)
+                    .addConditions(mLowLightConditions.get())
+                    .build());
+        }
+    }
+
+
+    @Override
+    public void onScreenTurnedOff() {
+        if (mSubscriptionToken != null) {
+            mLogger.d(TAG, "Screen turned off. Removing subscription to low light conditions.");
+
+            mConditionsMonitor.removeSubscription(mSubscriptionToken);
+            mSubscriptionToken = null;
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        if (mLowLightDreamService != null) {
+            // Note that the dream service is disabled by default. This prevents the dream from
+            // appearing in settings on devices that don't have it explicitly excluded (done in
+            // the settings overlay). Therefore, the component is enabled if it is to be used
+            // here.
+            mPackageManager.setComponentEnabledSetting(
+                    mLowLightDreamService,
+                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                    PackageManager.DONT_KILL_APP
+            );
+        } else {
+            // If there is no low light dream service, do not observe conditions.
+            return;
+        }
+
+        mScreenLifecycle.addObserver(this);
+        if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
+            onScreenTurnedOn();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java
new file mode 100644
index 0000000..fd6ce17
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java
@@ -0,0 +1,87 @@
+/*
+ * 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.lowlightclock;
+
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.util.settings.SecureSettings;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import javax.inject.Inject;
+
+/**
+ * Condition for monitoring if the screensaver setting is enabled.
+ */
+public class ScreenSaverEnabledCondition extends Condition {
+    private static final String TAG = ScreenSaverEnabledCondition.class.getSimpleName();
+
+    private final boolean mScreenSaverEnabledByDefaultConfig;
+    private final SecureSettings mSecureSettings;
+
+    private final ContentObserver mScreenSaverSettingObserver = new ContentObserver(null) {
+        @Override
+        public void onChange(boolean selfChange) {
+            updateScreenSaverEnabledSetting();
+        }
+    };
+
+    @Inject
+    public ScreenSaverEnabledCondition(@Application CoroutineScope scope, @Main Resources resources,
+            SecureSettings secureSettings) {
+        super(scope);
+        mScreenSaverEnabledByDefaultConfig = resources.getBoolean(
+                com.android.internal.R.bool.config_dreamsEnabledByDefault);
+        mSecureSettings = secureSettings;
+    }
+
+    @Override
+    protected void start() {
+        mSecureSettings.registerContentObserverForUserSync(
+                Settings.Secure.SCREENSAVER_ENABLED,
+                mScreenSaverSettingObserver, UserHandle.USER_CURRENT);
+        updateScreenSaverEnabledSetting();
+    }
+
+    @Override
+    protected void stop() {
+        mSecureSettings.unregisterContentObserverSync(mScreenSaverSettingObserver);
+    }
+
+    @Override
+    protected int getStartStrategy() {
+        return START_EAGERLY;
+    }
+
+    private void updateScreenSaverEnabledSetting() {
+        final boolean enabled = mSecureSettings.getIntForUser(
+                Settings.Secure.SCREENSAVER_ENABLED,
+                mScreenSaverEnabledByDefaultConfig ? 1 : 0,
+                UserHandle.USER_CURRENT) != 0;
+        if (!enabled) {
+            Log.i(TAG, "Disabling low-light clock because screen saver has been disabled");
+        }
+        updateCondition(enabled);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightLog.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightLog.kt
new file mode 100644
index 0000000..0819664c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightLog.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.lowlightclock.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for logging related to low light features. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class LowLightLog
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.java
new file mode 100644
index 0000000..c08be51
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.java
@@ -0,0 +1,150 @@
+/*
+ * 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.lowlightclock.dagger;
+
+import android.content.res.Resources;
+import android.hardware.Sensor;
+
+import com.android.dream.lowlight.dagger.LowLightDreamModule;
+import com.android.systemui.CoreStartable;
+import com.android.systemui.communal.DeviceInactiveCondition;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.LogBufferFactory;
+import com.android.systemui.lowlightclock.AmbientLightModeMonitor;
+import com.android.systemui.lowlightclock.DirectBootCondition;
+import com.android.systemui.lowlightclock.ForceLowLightCondition;
+import com.android.systemui.lowlightclock.LowLightCondition;
+import com.android.systemui.lowlightclock.LowLightDisplayController;
+import com.android.systemui.lowlightclock.LowLightMonitor;
+import com.android.systemui.lowlightclock.ScreenSaverEnabledCondition;
+import com.android.systemui.res.R;
+import com.android.systemui.shared.condition.Condition;
+
+import dagger.Binds;
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+
+import javax.inject.Named;
+
+@Module(includes = LowLightDreamModule.class)
+public abstract class LowLightModule {
+    public static final String Y_TRANSLATION_ANIMATION_OFFSET =
+            "y_translation_animation_offset";
+    public static final String Y_TRANSLATION_ANIMATION_DURATION_MILLIS =
+            "y_translation_animation_duration_millis";
+    public static final String ALPHA_ANIMATION_IN_START_DELAY_MILLIS =
+            "alpha_animation_in_start_delay_millis";
+    public static final String ALPHA_ANIMATION_DURATION_MILLIS =
+            "alpha_animation_duration_millis";
+    public static final String LOW_LIGHT_PRECONDITIONS = "low_light_preconditions";
+    public static final String LIGHT_SENSOR = "low_light_monitor_light_sensor";
+
+
+    /**
+     * Provides a {@link LogBuffer} for logs related to low-light features.
+     */
+    @Provides
+    @SysUISingleton
+    @LowLightLog
+    public static LogBuffer provideLowLightLogBuffer(LogBufferFactory factory) {
+        return factory.create("LowLightLog", 250);
+    }
+
+    @Binds
+    @IntoSet
+    @Named(LOW_LIGHT_PRECONDITIONS)
+    abstract Condition bindScreenSaverEnabledCondition(ScreenSaverEnabledCondition condition);
+
+    @Provides
+    @IntoSet
+    @Named(com.android.systemui.lowlightclock.dagger.LowLightModule.LOW_LIGHT_PRECONDITIONS)
+    static Condition provideLowLightCondition(LowLightCondition lowLightCondition,
+            DirectBootCondition directBootCondition) {
+        // Start lowlight if we are either in lowlight or in direct boot. The ordering of the
+        // conditions matters here since we don't want to start the lowlight condition if
+        // we are in direct boot mode.
+        return directBootCondition.or(lowLightCondition);
+    }
+
+    @Binds
+    @IntoSet
+    @Named(LOW_LIGHT_PRECONDITIONS)
+    abstract Condition bindForceLowLightCondition(ForceLowLightCondition condition);
+
+    @Binds
+    @IntoSet
+    @Named(LOW_LIGHT_PRECONDITIONS)
+    abstract Condition bindDeviceInactiveCondition(DeviceInactiveCondition condition);
+
+    @BindsOptionalOf
+    abstract LowLightDisplayController bindsLowLightDisplayController();
+
+    @BindsOptionalOf
+    @Named(LIGHT_SENSOR)
+    abstract Sensor bindsLightSensor();
+
+    @BindsOptionalOf
+    abstract AmbientLightModeMonitor.DebounceAlgorithm bindsDebounceAlgorithm();
+
+    /**
+     *
+     */
+    @Provides
+    @Named(Y_TRANSLATION_ANIMATION_OFFSET)
+    static int providesAnimationInOffset(@Main Resources resources) {
+        return resources.getDimensionPixelOffset(
+                R.dimen.low_light_clock_translate_animation_offset);
+    }
+
+    /**
+     *
+     */
+    @Provides
+    @Named(Y_TRANSLATION_ANIMATION_DURATION_MILLIS)
+    static long providesAnimationDurationMillis(@Main Resources resources) {
+        return resources.getInteger(R.integer.low_light_clock_translate_animation_duration_ms);
+    }
+
+    /**
+     *
+     */
+    @Provides
+    @Named(ALPHA_ANIMATION_IN_START_DELAY_MILLIS)
+    static long providesAlphaAnimationInStartDelayMillis(@Main Resources resources) {
+        return resources.getInteger(R.integer.low_light_clock_alpha_animation_in_start_delay_ms);
+    }
+
+    /**
+     *
+     */
+    @Provides
+    @Named(ALPHA_ANIMATION_DURATION_MILLIS)
+    static long providesAlphaAnimationDurationMillis(@Main Resources resources) {
+        return resources.getInteger(R.integer.low_light_clock_alpha_animation_duration_ms);
+    }
+    /** Inject into LowLightMonitor. */
+    @Binds
+    @IntoMap
+    @ClassKey(LowLightMonitor.class)
+    abstract CoreStartable bindLowLightMonitor(LowLightMonitor lowLightMonitor);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaDataRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaDataRepository.kt
index b6fd287..8e773a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaDataRepository.kt
@@ -36,10 +36,7 @@
 @SysUISingleton
 class MediaDataRepository
 @Inject
-constructor(
-    private val mediaFlags: MediaFlags,
-    dumpManager: DumpManager,
-) : Dumpable {
+constructor(private val mediaFlags: MediaFlags, dumpManager: DumpManager) : Dumpable {
 
     private val _mediaEntries: MutableStateFlow<Map<String, MediaData>> =
         MutableStateFlow(LinkedHashMap())
@@ -59,27 +56,6 @@
     }
 
     /**
-     * Marks the recommendation data as inactive.
-     *
-     * @return true if the recommendation was actually marked as inactive, false otherwise.
-     */
-    fun setRecommendationInactive(key: String): Boolean {
-        if (!mediaFlags.isPersistentSsCardEnabled()) {
-            Log.e(TAG, "Only persistent recommendation can be inactive!")
-            return false
-        }
-        if (DEBUG) Log.d(TAG, "Setting smartspace recommendation inactive")
-
-        if (smartspaceMediaData.value.targetId != key || !smartspaceMediaData.value.isValid()) {
-            // If this doesn't match, or we've already invalidated the data, no action needed
-            return false
-        }
-
-        setRecommendation(smartspaceMediaData.value.copy(isActive = false))
-        return true
-    }
-
-    /**
      * Marks the recommendation data as dismissed.
      *
      * @return true if the recommendation was dismissed or already inactive, false otherwise.
@@ -96,7 +72,7 @@
             setRecommendation(
                 SmartspaceMediaData(
                     targetId = smartspaceMediaData.value.targetId,
-                    instanceId = smartspaceMediaData.value.instanceId
+                    instanceId = smartspaceMediaData.value.instanceId,
                 )
             )
         }
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 96ef7d2..6ea161c 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
@@ -76,6 +76,7 @@
     private val _listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
     val listeners: Set<MediaDataManager.Listener>
         get() = _listeners.toSet()
+
     lateinit var mediaDataManager: MediaDataManager
 
     private val allEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
@@ -107,7 +108,7 @@
         data: MediaData,
         immediately: Boolean,
         receivedSmartspaceCardLatency: Int,
-        isSsReactivated: Boolean
+        isSsReactivated: Boolean,
     ) {
         if (oldKey != null && oldKey != key) {
             allEntries.remove(oldKey)
@@ -133,11 +134,9 @@
     override fun onSmartspaceMediaDataLoaded(
         key: String,
         data: SmartspaceMediaData,
-        shouldPrioritize: Boolean
+        shouldPrioritize: Boolean,
     ) {
-        // With persistent recommendation card, we could get a background update while inactive
-        // Otherwise, consider it an invalid update
-        if (!data.isActive && !mediaFlags.isPersistentSsCardEnabled()) {
+        if (!data.isActive) {
             Log.d(TAG, "Inactive recommendation data. Skip triggering.")
             return
         }
@@ -176,7 +175,7 @@
                 logger.logRecommendationActivated(
                     mediaData.appUid,
                     mediaData.packageName,
-                    mediaData.instanceId
+                    mediaData.instanceId,
                 )
                 listeners.forEach {
                     it.onMediaDataLoaded(
@@ -186,7 +185,7 @@
                         receivedSmartspaceCardLatency =
                             (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis)
                                 .toInt(),
-                        isSsReactivated = true
+                        isSsReactivated = true,
                     )
                 }
             }
@@ -201,7 +200,7 @@
         }
         logger.logRecommendationAdded(
             smartspaceMediaData.packageName,
-            smartspaceMediaData.instanceId
+            smartspaceMediaData.instanceId,
         )
         listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
     }
@@ -232,7 +231,7 @@
             smartspaceMediaData =
                 EMPTY_SMARTSPACE_MEDIA_DATA.copy(
                     targetId = smartspaceMediaData.targetId,
-                    instanceId = smartspaceMediaData.instanceId
+                    instanceId = smartspaceMediaData.instanceId,
                 )
         }
         listeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) }
@@ -286,7 +285,7 @@
             if (dismissIntent == null) {
                 Log.w(
                     TAG,
-                    "Cannot create dismiss action click action: extras missing dismiss_intent."
+                    "Cannot create dismiss action click action: extras missing dismiss_intent.",
                 )
             } else if (
                 dismissIntent.component?.className == EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME
@@ -297,20 +296,15 @@
                 broadcastSender.sendBroadcast(dismissIntent)
             }
 
-            if (mediaFlags.isPersistentSsCardEnabled()) {
-                smartspaceMediaData = smartspaceMediaData.copy(isActive = false)
-                mediaDataManager.setRecommendationInactive(smartspaceMediaData.targetId)
-            } else {
-                smartspaceMediaData =
-                    EMPTY_SMARTSPACE_MEDIA_DATA.copy(
-                        targetId = smartspaceMediaData.targetId,
-                        instanceId = smartspaceMediaData.instanceId,
-                    )
-                mediaDataManager.dismissSmartspaceRecommendation(
-                    smartspaceMediaData.targetId,
-                    delay = 0L,
+            smartspaceMediaData =
+                EMPTY_SMARTSPACE_MEDIA_DATA.copy(
+                    targetId = smartspaceMediaData.targetId,
+                    instanceId = smartspaceMediaData.instanceId,
                 )
-            }
+            mediaDataManager.dismissSmartspaceRecommendation(
+                smartspaceMediaData.targetId,
+                delay = 0L,
+            )
         }
     }
 
@@ -322,12 +316,7 @@
 
     /** Are there any media entries we should display? */
     fun hasAnyMediaOrRecommendation(): Boolean {
-        val hasSmartspace =
-            if (mediaFlags.isPersistentSsCardEnabled()) {
-                smartspaceMediaData.isValid()
-            } else {
-                smartspaceMediaData.isActive && smartspaceMediaData.isValid()
-            }
+        val hasSmartspace = smartspaceMediaData.isActive && smartspaceMediaData.isValid()
         return userEntries.isNotEmpty() || hasSmartspace
     }
 
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 f1f299a..3eac12d 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
@@ -70,8 +70,6 @@
 import com.android.systemui.media.controls.domain.resume.MediaResumeListener
 import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
 import com.android.systemui.media.controls.shared.MediaLogger
-import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
-import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
 import com.android.systemui.media.controls.shared.model.MediaAction
 import com.android.systemui.media.controls.shared.model.MediaButton
 import com.android.systemui.media.controls.shared.model.MediaData
@@ -769,23 +767,6 @@
         )
     }
 
-    /** Called when the recommendation card should no longer be visible in QQS or lockscreen */
-    override fun setRecommendationInactive(key: String) {
-        if (!mediaFlags.isPersistentSsCardEnabled()) {
-            Log.e(TAG, "Only persistent recommendation can be inactive!")
-            return
-        }
-        if (DEBUG) Log.d(TAG, "Setting smartspace recommendation inactive")
-
-        if (smartspaceMediaData.targetId != key || !smartspaceMediaData.isValid()) {
-            // If this doesn't match, or we've already invalidated the data, no action needed
-            return
-        }
-
-        smartspaceMediaData = smartspaceMediaData.copy(isActive = false)
-        notifySmartspaceMediaDataLoaded(smartspaceMediaData.targetId, smartspaceMediaData)
-    }
-
     private suspend fun loadMediaDataForResumption(
         userId: Int,
         desc: MediaDescription,
@@ -1268,13 +1249,21 @@
     }
 
     private fun getResumeMediaAction(action: Runnable): MediaAction {
+        val iconId =
+            if (Flags.mediaControlsUiUpdate()) {
+                R.drawable.ic_media_play_button
+            } else {
+                R.drawable.ic_media_play
+            }
         return MediaAction(
-            Icon.createWithResource(context, R.drawable.ic_media_play)
-                .setTint(themeText)
-                .loadDrawable(context),
+            Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
             action,
             context.getString(R.string.controls_media_resume),
-            context.getDrawable(R.drawable.ic_media_play_container),
+            if (Flags.mediaControlsUiUpdate()) {
+                context.getDrawable(R.drawable.ic_media_play_button_container)
+            } else {
+                context.getDrawable(R.drawable.ic_media_play_container)
+            },
         )
     }
 
@@ -1490,15 +1479,7 @@
         val dismissIntent =
             baseAction?.extras?.getParcelable(EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY) as Intent?
 
-        val isActive =
-            when {
-                !mediaFlags.isPersistentSsCardEnabled() -> true
-                baseAction == null -> true
-                else -> {
-                    val triggerSource = baseAction.extras?.getString(EXTRA_KEY_TRIGGER_SOURCE)
-                    triggerSource != EXTRA_VALUE_TRIGGER_PERIODIC
-                }
-            }
+        val isActive = true
 
         packageName(target)?.let {
             return SmartspaceMediaData(
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 68f1af3..4c0312f 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
@@ -103,7 +103,7 @@
         data: MediaData,
         immediately: Boolean,
         receivedSmartspaceCardLatency: Int,
-        isSsReactivated: Boolean
+        isSsReactivated: Boolean,
     ) {
         if (oldKey != null && oldKey != key) {
             mediaFilterRepository.removeMediaEntry(oldKey)
@@ -122,7 +122,7 @@
         mediaLogger.logMediaLoaded(data.instanceId, data.active, "loading media")
         mediaFilterRepository.addMediaDataLoadingState(
             MediaDataLoadingModel.Loaded(data.instanceId),
-            isUpdate
+            isUpdate,
         )
 
         // Notify listeners
@@ -132,11 +132,9 @@
     override fun onSmartspaceMediaDataLoaded(
         key: String,
         data: SmartspaceMediaData,
-        shouldPrioritize: Boolean
+        shouldPrioritize: Boolean,
     ) {
-        // With persistent recommendation card, we could get a background update while inactive
-        // Otherwise, consider it an invalid update
-        if (!data.isActive && !mediaFlags.isPersistentSsCardEnabled()) {
+        if (!data.isActive) {
             Log.d(TAG, "Inactive recommendation data. Skip triggering.")
             return
         }
@@ -179,7 +177,7 @@
                 logger.logRecommendationActivated(
                     mediaData.appUid,
                     mediaData.packageName,
-                    mediaData.instanceId
+                    mediaData.instanceId,
                 )
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Loaded(
@@ -187,13 +185,13 @@
                         receivedSmartspaceCardLatency =
                             (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis)
                                 .toInt(),
-                        isSsReactivated = true
+                        isSsReactivated = true,
                     )
                 )
                 mediaLogger.logMediaLoaded(
                     mediaData.instanceId,
                     mediaData.active,
-                    "reactivating media instead of smartspace"
+                    "reactivating media instead of smartspace",
                 )
                 listeners.forEach { listener ->
                     getKey(lastActiveId)?.let { lastActiveKey ->
@@ -205,7 +203,7 @@
                                 (systemClock.currentTimeMillis() -
                                         data.headphoneConnectionTimeMillis)
                                     .toInt(),
-                            isSsReactivated = true
+                            isSsReactivated = true,
                         )
                     }
                 }
@@ -222,7 +220,7 @@
         val smartspaceMediaData = mediaFilterRepository.smartspaceMediaData.value
         logger.logRecommendationAdded(
             smartspaceMediaData.packageName,
-            smartspaceMediaData.instanceId
+            smartspaceMediaData.instanceId,
         )
         mediaFilterRepository.setRecommendationsLoadingState(
             SmartspaceMediaLoadingModel.Loaded(key, shouldPrioritizeMutable)
@@ -268,7 +266,7 @@
             mediaFilterRepository.setRecommendation(
                 EMPTY_SMARTSPACE_MEDIA_DATA.copy(
                     targetId = smartspaceMediaData.targetId,
-                    instanceId = smartspaceMediaData.instanceId
+                    instanceId = smartspaceMediaData.instanceId,
                 )
             )
         }
@@ -317,12 +315,12 @@
                 val isUpdate = mediaFilterRepository.addSelectedUserMediaEntry(data)
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Loaded(data.instanceId),
-                    isUpdate
+                    isUpdate,
                 )
                 mediaLogger.logMediaLoaded(
                     data.instanceId,
                     data.active,
-                    "Re-adding $key after user change"
+                    "Re-adding $key after user change",
                 )
                 listenersCopy.forEach { listener -> listener.onMediaDataLoaded(key, null, data) }
             }
@@ -346,7 +344,7 @@
             if (dismissIntent == null) {
                 Log.w(
                     TAG,
-                    "Cannot create dismiss action click action: extras missing dismiss_intent."
+                    "Cannot create dismiss action click action: extras missing dismiss_intent.",
                 )
             } else if (
                 dismissIntent.component?.className == EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME
@@ -357,21 +355,16 @@
                 broadcastSender.sendBroadcast(dismissIntent)
             }
 
-            if (mediaFlags.isPersistentSsCardEnabled()) {
-                mediaFilterRepository.setRecommendation(smartspaceMediaData.copy(isActive = false))
-                mediaDataProcessor.setRecommendationInactive(smartspaceMediaData.targetId)
-            } else {
-                mediaFilterRepository.setRecommendation(
-                    EMPTY_SMARTSPACE_MEDIA_DATA.copy(
-                        targetId = smartspaceMediaData.targetId,
-                        instanceId = smartspaceMediaData.instanceId,
-                    )
+            mediaFilterRepository.setRecommendation(
+                EMPTY_SMARTSPACE_MEDIA_DATA.copy(
+                    targetId = smartspaceMediaData.targetId,
+                    instanceId = smartspaceMediaData.instanceId,
                 )
-                mediaDataProcessor.dismissSmartspaceRecommendation(
-                    smartspaceMediaData.targetId,
-                    delay = 0L,
-                )
-            }
+            )
+            mediaDataProcessor.dismissSmartspaceRecommendation(
+                smartspaceMediaData.targetId,
+                delay = 0L,
+            )
         }
     }
 
@@ -421,7 +414,7 @@
             get() =
                 SystemProperties.getLong(
                     "debug.sysui.smartspace_max_age",
-                    TimeUnit.MINUTES.toMillis(30)
+                    TimeUnit.MINUTES.toMillis(30),
                 )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
index a176e0c..8bb7303 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
@@ -43,7 +43,9 @@
 import android.text.TextUtils
 import android.util.Log
 import androidx.media.utils.MediaConstants
+import com.android.app.tracing.coroutines.asyncTraced as async
 import com.android.app.tracing.coroutines.traceCoroutine
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -67,7 +69,6 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
-import com.android.app.tracing.coroutines.asyncTraced as async
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.ensureActive
@@ -511,13 +512,21 @@
         sbn.notification.extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)
 
     private fun getResumeMediaAction(action: Runnable): MediaAction {
+        val iconId =
+            if (Flags.mediaControlsUiUpdate()) {
+                R.drawable.ic_media_play_button
+            } else {
+                R.drawable.ic_media_play
+            }
         return MediaAction(
-            Icon.createWithResource(context, R.drawable.ic_media_play)
-                .setTint(themeText)
-                .loadDrawable(context),
+            Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
             action,
             context.getString(R.string.controls_media_resume),
-            context.getDrawable(R.drawable.ic_media_play_container),
+            if (Flags.mediaControlsUiUpdate()) {
+                context.getDrawable(R.drawable.ic_media_play_button_container)
+            } else {
+                context.getDrawable(R.drawable.ic_media_play_container)
+            },
         )
     }
 
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 8099e59..c3182bf 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
@@ -56,7 +56,7 @@
         token: MediaSession.Token,
         appName: String,
         appIntent: PendingIntent,
-        packageName: String
+        packageName: String,
     )
 
     /** Dismiss a media entry. Returns false if the key was not found. */
@@ -68,9 +68,6 @@
      */
     fun dismissSmartspaceRecommendation(key: String, delay: Long)
 
-    /** Called when the recommendation card should no longer be visible in QQS or lockscreen */
-    fun setRecommendationInactive(key: String)
-
     /** Invoked when notification is removed. */
     fun onNotificationRemoved(key: String)
 
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 a524db4..1464849 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
@@ -71,8 +71,6 @@
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
 import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
 import com.android.systemui.media.controls.shared.MediaLogger
-import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
-import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
 import com.android.systemui.media.controls.shared.model.MediaAction
 import com.android.systemui.media.controls.shared.model.MediaButton
 import com.android.systemui.media.controls.shared.model.MediaData
@@ -613,14 +611,6 @@
         }
     }
 
-    /** Called when the recommendation card should no longer be visible in QQS or lockscreen */
-    fun setRecommendationInactive(key: String) {
-        if (mediaDataRepository.setRecommendationInactive(key)) {
-            val recommendation = mediaDataRepository.smartspaceMediaData.value
-            notifySmartspaceMediaDataLoaded(recommendation.targetId, recommendation)
-        }
-    }
-
     private suspend fun loadMediaDataForResumption(
         userId: Int,
         desc: MediaDescription,
@@ -1197,13 +1187,21 @@
     }
 
     private fun getResumeMediaAction(action: Runnable): MediaAction {
+        val iconId =
+            if (Flags.mediaControlsUiUpdate()) {
+                R.drawable.ic_media_play_button
+            } else {
+                R.drawable.ic_media_play
+            }
         return MediaAction(
-            Icon.createWithResource(context, R.drawable.ic_media_play)
-                .setTint(themeText)
-                .loadDrawable(context),
+            Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
             action,
             context.getString(R.string.controls_media_resume),
-            context.getDrawable(R.drawable.ic_media_play_container),
+            if (Flags.mediaControlsUiUpdate()) {
+                context.getDrawable(R.drawable.ic_media_play_button_container)
+            } else {
+                context.getDrawable(R.drawable.ic_media_play_container)
+            },
         )
     }
 
@@ -1451,15 +1449,7 @@
                 ?.extras
                 ?.getParcelable(EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY, Intent::class.java)
 
-        val isActive =
-            when {
-                !mediaFlags.isPersistentSsCardEnabled() -> true
-                baseAction == null -> true
-                else -> {
-                    val triggerSource = baseAction.extras?.getString(EXTRA_KEY_TRIGGER_SOURCE)
-                    triggerSource != EXTRA_VALUE_TRIGGER_PERIODIC
-                }
-            }
+        val isActive = true
 
         packageName(target)?.let {
             return 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 39cedc3..684a560 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
@@ -180,30 +180,6 @@
         mediaListeners.remove(key)?.destroy()
     }
 
-    override fun onSmartspaceMediaDataLoaded(
-        key: String,
-        data: SmartspaceMediaData,
-        shouldPrioritize: Boolean,
-    ) {
-        if (!mediaFlags.isPersistentSsCardEnabled()) return
-
-        // First check if we already have a listener
-        recommendationListeners.get(key)?.let {
-            if (!it.destroyed) {
-                it.recommendationData = data
-                return
-            }
-        }
-
-        // Otherwise, create a new one
-        recommendationListeners[key] = RecommendationListener(key, data)
-    }
-
-    override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
-        if (!mediaFlags.isPersistentSsCardEnabled()) return
-        recommendationListeners.remove(key)?.destroy()
-    }
-
     fun isTimedOut(key: String): Boolean {
         return mediaListeners[key]?.timedOut ?: false
     }
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 270ab72..891b852 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
@@ -41,14 +41,12 @@
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates business logic for media pipeline. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MediaCarouselInteractor
 @Inject
@@ -70,7 +68,7 @@
         combine(
                 mediaFilterRepository.selectedUserEntries,
                 mediaFilterRepository.smartspaceMediaData,
-                mediaFilterRepository.reactivatedId
+                mediaFilterRepository.reactivatedId,
             ) { entries, smartspaceMediaData, reactivatedKey ->
                 entries.any { it.value.active } ||
                     (smartspaceMediaData.isActive &&
@@ -86,14 +84,10 @@
     val hasAnyMediaOrRecommendation: StateFlow<Boolean> =
         combine(
                 mediaFilterRepository.selectedUserEntries,
-                mediaFilterRepository.smartspaceMediaData
+                mediaFilterRepository.smartspaceMediaData,
             ) { entries, smartspaceMediaData ->
                 entries.isNotEmpty() ||
-                    (if (mediaFlags.isPersistentSsCardEnabled()) {
-                        smartspaceMediaData.isValid()
-                    } else {
-                        smartspaceMediaData.isActive && smartspaceMediaData.isValid()
-                    })
+                    (smartspaceMediaData.isActive && smartspaceMediaData.isValid())
             }
             .stateIn(
                 scope = applicationScope,
@@ -170,7 +164,7 @@
         token: MediaSession.Token,
         appName: String,
         appIntent: PendingIntent,
-        packageName: String
+        packageName: String,
     ) {
         mediaDataProcessor.addResumptionControls(
             userId,
@@ -179,7 +173,7 @@
             token,
             appName,
             appIntent,
-            packageName
+            packageName,
         )
     }
 
@@ -195,8 +189,6 @@
         return mediaDataProcessor.dismissSmartspaceRecommendation(key, delay)
     }
 
-    override fun setRecommendationInactive(key: String) = unsupported
-
     override fun onNotificationRemoved(key: String) {
         mediaDataProcessor.onNotificationRemoved(key)
     }
@@ -230,7 +222,7 @@
         mediaFilterRepository.logSmartspaceCardSeen(
             MediaSmartspaceLogger.getSurface(location),
             visibleIndex,
-            isMediaCardUpdate
+            isMediaCardUpdate,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt
index 95ca11c..ec329d3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt
@@ -18,7 +18,7 @@
 
 import android.content.Context
 import android.graphics.drawable.Drawable
-import com.android.systemui.Flags.mediaControlsDrawablesReuse
+import com.android.systemui.Flags.mediaControlsDrawablesReuseBugfix
 import com.android.systemui.res.R
 
 object MediaControlDrawables {
@@ -34,21 +34,21 @@
     private var homeDevices: Drawable? = null
 
     fun getNextIcon(context: Context): Drawable? {
-        if (!mediaControlsDrawablesReuse()) {
+        if (!mediaControlsDrawablesReuseBugfix()) {
             return context.getDrawable(R.drawable.ic_media_next)
         }
         return nextIcon ?: context.getDrawable(R.drawable.ic_media_next).also { nextIcon = it }
     }
 
     fun getPrevIcon(context: Context): Drawable? {
-        if (!mediaControlsDrawablesReuse()) {
+        if (!mediaControlsDrawablesReuseBugfix()) {
             return context.getDrawable(R.drawable.ic_media_prev)
         }
         return prevIcon ?: context.getDrawable(R.drawable.ic_media_prev).also { prevIcon = it }
     }
 
     fun getLeAudioSharing(context: Context): Drawable? {
-        if (!mediaControlsDrawablesReuse()) {
+        if (!mediaControlsDrawablesReuseBugfix()) {
             return context.getDrawable(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
         }
         return leAudioSharing
@@ -58,7 +58,7 @@
     }
 
     fun getAntenna(context: Context): Drawable? {
-        if (!mediaControlsDrawablesReuse()) {
+        if (!mediaControlsDrawablesReuseBugfix()) {
             return context.getDrawable(R.drawable.settings_input_antenna)
         }
         return antenna
@@ -66,7 +66,7 @@
     }
 
     fun getGroupDevice(context: Context): Drawable? {
-        if (!mediaControlsDrawablesReuse()) {
+        if (!mediaControlsDrawablesReuseBugfix()) {
             return context.getDrawable(com.android.settingslib.R.drawable.ic_media_group_device)
         }
         return groupDevice
@@ -76,7 +76,7 @@
     }
 
     fun getHomeDevices(context: Context): Drawable? {
-        if (!mediaControlsDrawablesReuse()) {
+        if (!mediaControlsDrawablesReuseBugfix()) {
             return context.getDrawable(R.drawable.ic_media_home_devices)
         }
         return homeDevices
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 4716119..f05029b 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
@@ -106,7 +106,6 @@
 import javax.inject.Provider
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.collectLatest
@@ -127,7 +126,6 @@
  * Class that is responsible for keeping the view carousel up to date. This also handles changes in
  * state and applies them to the media carousel like the expansion.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MediaCarouselController
 @Inject
@@ -616,12 +614,8 @@
                             logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
                         }
                     } else {
-                        if (!mediaFlags.isPersistentSsCardEnabled()) {
-                            // Handle update to inactive as a removal
-                            onSmartspaceMediaDataRemoved(data.targetId, immediately = true)
-                        } else {
-                            addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
-                        }
+                        // Handle update to inactive as a removal
+                        onSmartspaceMediaDataRemoved(data.targetId, immediately = true)
                     }
                     MediaPlayerData.isSwipedAway = false
                 }
@@ -1127,18 +1121,6 @@
         traceSection("MediaCarouselController#addSmartspaceMediaRecommendations") {
             if (DEBUG) Log.d(TAG, "Updating smartspace target in carousel")
             MediaPlayerData.getMediaPlayer(key)?.let {
-                if (mediaFlags.isPersistentSsCardEnabled()) {
-                    // The card exists, but could have changed active state, so update for sorting
-                    MediaPlayerData.addMediaRecommendation(
-                        key,
-                        data,
-                        it,
-                        shouldPrioritize,
-                        systemClock,
-                        debugLogger,
-                        update = true,
-                    )
-                }
                 Log.w(TAG, "Skip adding smartspace target in carousel")
                 return
             }
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 dccf61d..2bf6a10 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
@@ -435,6 +435,12 @@
         }
         this.mIsSeekBarEnabled = isSeekBarEnabled;
         updateSeekBarVisibility();
+        mMainExecutor.execute(() -> {
+            if (!mMetadataAnimationHandler.isRunning()) {
+                // Trigger a state refresh so that we immediately update visibilities.
+                mMediaViewController.refreshState();
+            }
+        });
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index a6aa159..1f70ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -67,7 +67,6 @@
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -99,7 +98,6 @@
  * This manager is responsible for placement of the unique media view between the different hosts
  * and animate the positions of the views to achieve seamless transitions.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MediaHierarchyManager
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 975f8f4..c00e14c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -225,6 +225,12 @@
                 if (isSeekBarEnabled == enabled) return
                 isSeekBarEnabled = enabled
                 MediaControlViewBinder.updateSeekBarVisibility(expandedLayout, isSeekBarEnabled)
+                mainExecutor.execute {
+                    if (!metadataAnimationHandler.isRunning) {
+                        // Trigger a state refresh so that we immediately update visibilities.
+                        refreshState()
+                    }
+                }
             }
         }
 
@@ -899,7 +905,11 @@
             // If the view isn't bound, we can drop the animation, otherwise we'll execute it
             animateNextStateChange = false
             if (transitionLayout == null) {
-                logger.logMediaLocation("setCurrentState: view not bound", startLocation, endLocation)
+                logger.logMediaLocation(
+                    "setCurrentState: view not bound",
+                    startLocation,
+                    endLocation,
+                )
                 return
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index b2137af..4bdcfea6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -167,14 +167,11 @@
     ): MediaCommonViewModel.MediaRecommendations {
         return mediaRecs?.copy(
             key = commonModel.recsLoadingModel.key,
-            loadingEnabled =
-                interactor.isRecommendationActive() || mediaFlags.isPersistentSsCardEnabled(),
+            loadingEnabled = interactor.isRecommendationActive(),
         )
             ?: MediaCommonViewModel.MediaRecommendations(
                     key = commonModel.recsLoadingModel.key,
-                    loadingEnabled =
-                        interactor.isRecommendationActive() ||
-                            mediaFlags.isPersistentSsCardEnabled(),
+                    loadingEnabled = interactor.isRecommendationActive(),
                     recsViewModel = recommendationsViewModel,
                     onAdded = { commonViewModel ->
                         mediaLogger.logMediaRecommendationCardAdded(
@@ -217,9 +214,7 @@
         commonViewModel: MediaCommonViewModel.MediaRecommendations
     ) {
         if (!interactor.isRecommendationActive()) {
-            if (!mediaFlags.isPersistentSsCardEnabled()) {
-                commonViewModel.onRemoved(true)
-            }
+            commonViewModel.onRemoved(true)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index ac60c47..172998e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -49,10 +49,6 @@
     /** Check whether to get progress information for resume players */
     fun isResumeProgressEnabled() = featureFlags.isEnabled(FlagsClassic.MEDIA_RESUME_PROGRESS)
 
-    /** If true, do not automatically dismiss the recommendation card */
-    fun isPersistentSsCardEnabled() =
-        featureFlags.isEnabled(FlagsClassic.MEDIA_RETAIN_RECOMMENDATIONS)
-
     /** Check whether we allow remote media to generate resume controls */
     fun isRemoteResumeAllowed() = featureFlags.isEnabled(FlagsClassic.MEDIA_REMOTE_RESUME)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
index 4496b25..7b1c62e2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
@@ -36,6 +36,7 @@
     private final String mTitle;
     @MediaItemType
     private final int mMediaItemType;
+    private final boolean mIsFirstDeviceInGroup;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
@@ -54,7 +55,18 @@
      * name.
      */
     public static MediaItem createDeviceMediaItem(@NonNull MediaDevice device) {
-        return new MediaItem(device, device.getName(), MediaItemType.TYPE_DEVICE);
+        return new MediaItem(device, device.getName(), MediaItemType.TYPE_DEVICE, false);
+    }
+
+    /**
+     * Returns a new {@link MediaItemType#TYPE_DEVICE} {@link MediaItem} with its {@link
+     * #getMediaDevice() media device} set to {@code device} and its title set to {@code device}'s
+     * name.
+     */
+    public static MediaItem createDeviceMediaItem(
+            @NonNull MediaDevice device, boolean isFirstDeviceInGroup) {
+        return new MediaItem(
+            device, device.getName(), MediaItemType.TYPE_DEVICE, isFirstDeviceInGroup);
     }
 
     /**
@@ -63,7 +75,10 @@
      */
     public static MediaItem createPairNewDeviceMediaItem() {
         return new MediaItem(
-                /* device */ null, /* title */ null, MediaItemType.TYPE_PAIR_NEW_DEVICE);
+                /* device */ null,
+                /* title */ null,
+                MediaItemType.TYPE_PAIR_NEW_DEVICE,
+                /* mIsFirstDeviceInGroup */ false);
     }
 
     /**
@@ -71,14 +86,22 @@
      * title and a {@code null} {@link #getMediaDevice() media device}.
      */
     public static MediaItem createGroupDividerMediaItem(@Nullable String title) {
-        return new MediaItem(/* device */ null, title, MediaItemType.TYPE_GROUP_DIVIDER);
+        return new MediaItem(
+            /* device */ null,
+            title,
+            MediaItemType.TYPE_GROUP_DIVIDER,
+            /* misFirstDeviceInGroup */ false);
     }
 
     private MediaItem(
-            @Nullable MediaDevice device, @Nullable String title, @MediaItemType int type) {
+            @Nullable MediaDevice device,
+            @Nullable String title,
+            @MediaItemType int type,
+            boolean isFirstDeviceInGroup) {
         this.mMediaDeviceOptional = Optional.ofNullable(device);
         this.mTitle = title;
         this.mMediaItemType = type;
+        this.mIsFirstDeviceInGroup = isFirstDeviceInGroup;
     }
 
     public Optional<MediaDevice> getMediaDevice() {
@@ -106,4 +129,8 @@
     public int getMediaItemType() {
         return mMediaItemType;
     }
+
+    public boolean isFirstDeviceInGroup() {
+        return mIsFirstDeviceInGroup;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 0ada931..52b3c3ec 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -21,6 +21,7 @@
 import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
 
 import android.annotation.DrawableRes;
+import android.annotation.StringRes;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.drawable.AnimatedVectorDrawable;
@@ -28,7 +29,6 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.CheckBox;
 import android.widget.TextView;
 
@@ -39,6 +39,7 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.media.flags.Flags;
 import com.android.settingslib.media.LocalMediaManager.MediaDeviceState;
 import com.android.settingslib.media.MediaDevice;
 import com.android.systemui.res.R;
@@ -56,6 +57,7 @@
     private static final float DEVICE_DISCONNECTED_ALPHA = 0.5f;
     private static final float DEVICE_CONNECTED_ALPHA = 1f;
     protected List<MediaItem> mMediaItemList = new CopyOnWriteArrayList<>();
+    private boolean mShouldGroupSelectedMediaItems = Flags.enableOutputSwitcherSessionGrouping();
 
     public MediaOutputAdapter(MediaSwitchingController controller) {
         super(controller);
@@ -66,6 +68,12 @@
     public void updateItems() {
         mMediaItemList.clear();
         mMediaItemList.addAll(mController.getMediaItemList());
+        if (mShouldGroupSelectedMediaItems) {
+            if (mController.getSelectedMediaDevice().size() == 1) {
+                // Don't group devices if initially there isn't more than one selected.
+                mShouldGroupSelectedMediaItems = false;
+            }
+        }
         notifyDataSetChanged();
     }
 
@@ -102,7 +110,7 @@
                 break;
             case MediaItem.MediaItemType.TYPE_DEVICE:
                 ((MediaDeviceViewHolder) viewHolder).onBind(
-                        currentMediaItem.getMediaDevice().get(),
+                        currentMediaItem,
                         position);
                 break;
             default:
@@ -142,8 +150,8 @@
             super(view);
         }
 
-        @Override
-        void onBind(MediaDevice device, int position) {
+        void onBind(MediaItem mediaItem, int position) {
+            MediaDevice device = mediaItem.getMediaDevice().get();
             super.onBind(device, position);
             boolean isMutingExpectedDeviceExist = mController.hasMutingExpectedDevice();
             final boolean currentlyConnected = isCurrentlyConnected(device);
@@ -151,6 +159,7 @@
             if (mCurrentActivePosition == position) {
                 mCurrentActivePosition = -1;
             }
+            mItemLayout.setVisibility(View.VISIBLE);
             mStatusIcon.setVisibility(View.GONE);
             enableFocusPropertyForView(mContainerLayout);
 
@@ -159,12 +168,12 @@
                         && !mController.hasAdjustVolumeUserRestriction()) {
                     setUpDeviceIcon(device);
                     updateProgressBarColor();
-                    setSingleLineLayout(getItemTitle(device), false /* showSeekBar*/,
+                    setSingleLineLayout(device.getName(), false /* showSeekBar*/,
                             true /* showProgressBar */, false /* showCheckBox */,
                             false /* showEndTouchArea */);
                 } else {
                     setUpDeviceIcon(device);
-                    setSingleLineLayout(getItemTitle(device));
+                    setSingleLineLayout(device.getName());
                 }
             } else {
                 // Set different layout for each device
@@ -173,8 +182,32 @@
                     updateUnmutedVolumeIcon(device);
                     mCurrentActivePosition = position;
                     updateFullItemClickListener(v -> onItemClick(v, device));
-                    setSingleLineLayout(getItemTitle(device));
+                    setSingleLineLayout(device.getName());
                     initFakeActiveDevice(device);
+                } else if (mShouldGroupSelectedMediaItems
+                        && mController.getSelectedMediaDevice().size() > 1
+                        && isDeviceIncluded(mController.getSelectedMediaDevice(), device)) {
+                    if (!mediaItem.isFirstDeviceInGroup()) {
+                        mItemLayout.setVisibility(View.GONE);
+                        mEndTouchArea.setVisibility(View.GONE);
+                    } else {
+                        String sessionName = mController.getSessionName().toString();
+                        updateUnmutedVolumeIcon(null);
+                        updateEndClickAreaWithIcon(
+                                v -> {
+                                    mShouldGroupSelectedMediaItems = false;
+                                    notifyDataSetChanged();
+                                },
+                                R.drawable.media_output_item_expand_group,
+                                R.string.accessibility_expand_group);
+                        disableFocusPropertyForView(mContainerLayout);
+                        setUpContentDescriptionForView(mSeekBar, mContext.getString(
+                                R.string.accessibility_cast_name, sessionName));
+                        setSingleLineLayout(sessionName, true /* showSeekBar */,
+                                false /* showProgressBar */, false /* showCheckBox */,
+                                true /* showEndTouchArea */);
+                        initGroupSeekbar(isCurrentSeekbarInvisible);
+                    }
                 } else if (device.hasSubtext()) {
                     boolean isActiveWithOngoingSession =
                             (device.hasOngoingSession() && (currentlyConnected || isDeviceIncluded(
@@ -185,14 +218,14 @@
                         mCurrentActivePosition = position;
                         updateUnmutedVolumeIcon(device);
                         mSubTitleText.setText(device.getSubtextString());
-                        updateTwoLineLayoutContentAlpha(DEVICE_CONNECTED_ALPHA);
+                        updateContentAlpha(DEVICE_CONNECTED_ALPHA);
                         updateEndClickAreaAsSessionEditing(device,
                                 isHost ? R.drawable.media_output_status_edit_session
                                         : R.drawable.ic_sound_bars_anim);
-                        setTwoLineLayout(device, null /* title */, true /* bFocused */,
+                        setTwoLineLayout(device.getName() /* title */,
                                 true /* showSeekBar */, false /* showProgressBar */,
                                 true /* showSubtitle */, false /* showStatus */,
-                                true /* showEndTouchArea */, false /* isFakeActive */);
+                                true /* showEndTouchArea */);
                         initSeekbar(device, isCurrentSeekbarInvisible);
                     } else {
                         if (currentlyConnected) {
@@ -211,27 +244,26 @@
                         if (deviceStatusIcon != null) {
                             updateDeviceStatusIcon(deviceStatusIcon);
                         }
-                        updateTwoLineLayoutContentAlpha(
+                        updateContentAlpha(
                                 updateClickActionBasedOnSelectionBehavior(device)
                                         ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
-                        setTwoLineLayout(device, currentlyConnected /* bFocused */,
+                        setTwoLineLayout(device.getName(),
                                 currentlyConnected  /* showSeekBar */,
                                 false /* showProgressBar */, true /* showSubtitle */,
-                                deviceStatusIcon != null /* showStatus */,
-                                false /* isFakeActive */);
+                                deviceStatusIcon != null /* showStatus */);
                     }
                 } else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
                     setUpDeviceIcon(device);
                     updateConnectionFailedStatusIcon();
                     mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
                     updateFullItemClickListener(v -> onItemClick(v, device));
-                    setTwoLineLayout(device, false /* bFocused */, false /* showSeekBar */,
+                    setTwoLineLayout(device.getName(), false /* showSeekBar */,
                             false /* showProgressBar */, true /* showSubtitle */,
-                            true /* showStatus */, false /*isFakeActive*/);
+                            true /* showStatus */);
                 } else if (device.getState() == MediaDeviceState.STATE_GROUPING) {
                     setUpDeviceIcon(device);
                     updateProgressBarColor();
-                    setSingleLineLayout(getItemTitle(device), false /* showSeekBar*/,
+                    setSingleLineLayout(device.getName(), false /* showSeekBar*/,
                             true /* showProgressBar */, false /* showCheckBox */,
                             false /* showEndTouchArea */);
                 } else if (mController.getSelectedMediaDevice().size() > 1
@@ -239,14 +271,16 @@
                     // selected device in group
                     boolean isDeviceDeselectable = isDeviceIncluded(
                             mController.getDeselectableMediaDevice(), device);
+                    boolean showEndArea = !Flags.enableOutputSwitcherSessionGrouping()
+                            || isDeviceDeselectable;
                     updateUnmutedVolumeIcon(device);
                     updateGroupableCheckBox(true, isDeviceDeselectable, device);
                     updateEndClickArea(device, isDeviceDeselectable);
                     disableFocusPropertyForView(mContainerLayout);
                     setUpContentDescriptionForView(mSeekBar, device);
-                    setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+                    setSingleLineLayout(device.getName(), true /* showSeekBar */,
                             false /* showProgressBar */, true /* showCheckBox */,
-                            true /* showEndTouchArea */);
+                            showEndArea /* showEndTouchArea */);
                     initSeekbar(device, isCurrentSeekbarInvisible);
                 } else if (!mController.hasAdjustVolumeUserRestriction()
                         && currentlyConnected) {
@@ -256,7 +290,7 @@
                         // mark as disconnected and set special click listener
                         setUpDeviceIcon(device);
                         updateFullItemClickListener(v -> cancelMuteAwaitConnection());
-                        setSingleLineLayout(getItemTitle(device));
+                        setSingleLineLayout(device.getName());
                     } else if (device.hasOngoingSession()) {
                         mCurrentActivePosition = position;
                         updateUnmutedVolumeIcon(device);
@@ -264,7 +298,7 @@
                                 ? R.drawable.media_output_status_edit_session
                                 : R.drawable.ic_sound_bars_anim);
                         mEndClickIcon.setVisibility(View.VISIBLE);
-                        setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+                        setSingleLineLayout(device.getName(), true /* showSeekBar */,
                                 false /* showProgressBar */, false /* showCheckBox */,
                                 true /* showEndTouchArea */);
                         initSeekbar(device, isCurrentSeekbarInvisible);
@@ -279,7 +313,7 @@
                         updateEndClickArea(device, isDeviceDeselectable);
                         disableFocusPropertyForView(mContainerLayout);
                         setUpContentDescriptionForView(mSeekBar, device);
-                        setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+                        setSingleLineLayout(device.getName(), true /* showSeekBar */,
                                 false /* showProgressBar */, true /* showCheckBox */,
                                 true /* showEndTouchArea */);
                         initSeekbar(device, isCurrentSeekbarInvisible);
@@ -288,7 +322,7 @@
                         disableFocusPropertyForView(mContainerLayout);
                         setUpContentDescriptionForView(mSeekBar, device);
                         mCurrentActivePosition = position;
-                        setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+                        setSingleLineLayout(device.getName(), true /* showSeekBar */,
                                 false /* showProgressBar */, false /* showCheckBox */,
                                 false /* showEndTouchArea */);
                         initSeekbar(device, isCurrentSeekbarInvisible);
@@ -299,12 +333,12 @@
                     updateGroupableCheckBox(false, true, device);
                     updateEndClickArea(device, true);
                     updateFullItemClickListener(v -> onItemClick(v, device));
-                    setSingleLineLayout(getItemTitle(device), false /* showSeekBar */,
+                    setSingleLineLayout(device.getName(), false /* showSeekBar */,
                             false /* showProgressBar */, true /* showCheckBox */,
                             true /* showEndTouchArea */);
                 } else {
                     setUpDeviceIcon(device);
-                    setSingleLineLayout(getItemTitle(device));
+                    setSingleLineLayout(device.getName());
                     Drawable deviceStatusIcon =
                             device.hasOngoingSession() ? mContext.getDrawable(
                                     R.drawable.ic_sound_bars_anim)
@@ -315,7 +349,7 @@
                         updateDeviceStatusIcon(deviceStatusIcon);
                         mStatusIcon.setVisibility(View.VISIBLE);
                     }
-                    updateSingleLineLayoutContentAlpha(
+                    updateContentAlpha(
                             updateClickActionBasedOnSelectionBehavior(device)
                                     ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
                 }
@@ -329,33 +363,37 @@
                     ColorStateList(states, colors));
         }
 
-        private void updateTwoLineLayoutContentAlpha(float alphaValue) {
-            mSubTitleText.setAlpha(alphaValue);
-            mTitleIcon.setAlpha(alphaValue);
-            mTwoLineTitleText.setAlpha(alphaValue);
-            mStatusIcon.setAlpha(alphaValue);
-        }
-
-        private void updateSingleLineLayoutContentAlpha(float alphaValue) {
+        private void updateContentAlpha(float alphaValue) {
             mTitleIcon.setAlpha(alphaValue);
             mTitleText.setAlpha(alphaValue);
+            mSubTitleText.setAlpha(alphaValue);
             mStatusIcon.setAlpha(alphaValue);
         }
 
         private void updateEndClickAreaAsSessionEditing(MediaDevice device, @DrawableRes int id) {
-            mEndClickIcon.setOnClickListener(null);
-            mEndTouchArea.setOnClickListener(null);
+            updateEndClickAreaWithIcon(
+                    v -> mController.tryToLaunchInAppRoutingIntent(device.getId(), v),
+                    id,
+                    R.string.accessibility_open_application);
+        }
+
+        private void updateEndClickAreaWithIcon(View.OnClickListener clickListener,
+                @DrawableRes int iconDrawableId,
+                @StringRes int accessibilityStringId) {
             updateEndClickAreaColor(mController.getColorSeekbarProgress());
             mEndClickIcon.setImageTintList(
                     ColorStateList.valueOf(mController.getColorItemContent()));
-            mEndClickIcon.setOnClickListener(
-                    v -> mController.tryToLaunchInAppRoutingIntent(device.getId(), v));
+            mEndClickIcon.setOnClickListener(clickListener);
             mEndTouchArea.setOnClickListener(v -> mEndClickIcon.performClick());
-            Drawable drawable = mContext.getDrawable(id);
+            Drawable drawable = mContext.getDrawable(iconDrawableId);
             mEndClickIcon.setImageDrawable(drawable);
             if (drawable instanceof AnimatedVectorDrawable) {
                 ((AnimatedVectorDrawable) drawable).start();
             }
+            if (Flags.enableOutputSwitcherSessionGrouping()) {
+                setUpContentDescriptionForView(
+                        mEndClickIcon, mContext.getString(accessibilityStringId));
+            }
         }
 
         public void updateEndClickAreaColor(int color) {
@@ -427,8 +465,6 @@
             mTitleIcon.setImageDrawable(addDrawable);
             mTitleIcon.setImageTintList(
                     ColorStateList.valueOf(mController.getColorItemContent()));
-            mIconAreaLayout.setBackgroundTintList(
-                    ColorStateList.valueOf(mController.getColorItemBackground()));
             mContainerLayout.setOnClickListener(mController::launchBluetoothPairing);
         }
 
@@ -489,18 +525,16 @@
         }
 
         private void setUpContentDescriptionForView(View view, MediaDevice device) {
-            view.setContentDescription(
+            setUpContentDescriptionForView(
+                    view,
                     mContext.getString(device.getDeviceType()
                             == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
                             ? R.string.accessibility_bluetooth_name
                             : R.string.accessibility_cast_name, device.getName()));
-            view.setAccessibilityDelegate(new View.AccessibilityDelegate() {
-                public void onInitializeAccessibilityNodeInfo(View host,
-                        AccessibilityNodeInfo info) {
-                    super.onInitializeAccessibilityNodeInfo(host, info);
-                    host.setOnClickListener(null);
-                }
-            });
+        }
+
+        protected void setUpContentDescriptionForView(View view, String description) {
+            view.setContentDescription(description);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 1a2238c..ee2d8aa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -16,14 +16,11 @@
 
 package com.android.systemui.media.dialog;
 
-import static com.android.systemui.media.dialog.MediaOutputSeekbar.VOLUME_PERCENTAGE_SCALE_SIZE;
-
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.app.WallpaperColors;
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.graphics.Typeface;
 import android.graphics.drawable.ClipDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
@@ -37,7 +34,6 @@
 import android.widget.CheckBox;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.ProgressBar;
 import android.widget.SeekBar;
 import android.widget.TextView;
@@ -46,6 +42,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.media.flags.Flags;
 import com.android.settingslib.media.InputMediaDevice;
 import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.utils.ThreadUtils;
@@ -95,10 +92,6 @@
         mController.setCurrentColorScheme(wallpaperColors, isDarkTheme);
     }
 
-    CharSequence getItemTitle(MediaDevice device) {
-        return device.getName();
-    }
-
     boolean isCurrentlyConnected(MediaDevice device) {
         return TextUtils.equals(device.getId(),
                 mController.getCurrentConnectedMediaDevice().getId())
@@ -138,12 +131,10 @@
         final FrameLayout mItemLayout;
         final FrameLayout mIconAreaLayout;
         final TextView mTitleText;
-        final TextView mTwoLineTitleText;
         final TextView mSubTitleText;
         final TextView mVolumeValueText;
         final ImageView mTitleIcon;
         final ProgressBar mProgressBar;
-        final LinearLayout mTwoLineLayout;
         final ImageView mStatusIcon;
         final CheckBox mCheckBox;
         final ViewGroup mEndTouchArea;
@@ -161,8 +152,6 @@
             mItemLayout = view.requireViewById(R.id.item_layout);
             mTitleText = view.requireViewById(R.id.title);
             mSubTitleText = view.requireViewById(R.id.subtitle);
-            mTwoLineLayout = view.requireViewById(R.id.two_line_layout);
-            mTwoLineTitleText = view.requireViewById(R.id.two_line_title);
             mTitleIcon = view.requireViewById(R.id.title_icon);
             mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress);
             mSeekBar = view.requireViewById(R.id.volume_seekbar);
@@ -185,9 +174,8 @@
             mContainerLayout.setContentDescription(null);
             mTitleText.setTextColor(mController.getColorItemContent());
             mSubTitleText.setTextColor(mController.getColorItemContent());
-            mSubTitleText.setSelected(true);
-            mTwoLineTitleText.setTextColor(mController.getColorItemContent());
             mVolumeValueText.setTextColor(mController.getColorItemContent());
+            mIconAreaLayout.setBackground(null);
             mSeekBar.setProgressTintList(
                     ColorStateList.valueOf(mController.getColorSeekbarProgress()));
         }
@@ -198,7 +186,6 @@
 
         void setSingleLineLayout(CharSequence title, boolean showSeekBar,
                 boolean showProgressBar, boolean showCheckBox, boolean showEndTouchArea) {
-            mTwoLineLayout.setVisibility(View.GONE);
             boolean isActive = showSeekBar || showProgressBar;
             if (!mCornerAnimator.isRunning()) {
                 final Drawable backgroundDrawable =
@@ -216,10 +203,6 @@
             mItemLayout.setBackgroundTintList(
                     ColorStateList.valueOf(isActive ? mController.getColorConnectedItemBackground()
                             : mController.getColorItemBackground()));
-            mIconAreaLayout.setBackgroundTintList(
-                    ColorStateList.valueOf(showSeekBar ? mController.getColorSeekbarProgress()
-                            : showProgressBar ? mController.getColorConnectedItemBackground()
-                                    : mController.getColorItemBackground()));
             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
             mSeekBar.setAlpha(1);
             mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
@@ -227,43 +210,37 @@
                 mSeekBar.resetVolume();
             }
             mTitleText.setText(title);
-            mTitleText.setVisibility(View.VISIBLE);
             mCheckBox.setVisibility(showCheckBox ? View.VISIBLE : View.GONE);
             mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
+            if (Flags.enableOutputSwitcherSessionGrouping()) {
+                mEndClickIcon.setVisibility(
+                        !showCheckBox && showEndTouchArea ? View.VISIBLE : View.GONE);
+            }
             ViewGroup.MarginLayoutParams params =
                     (ViewGroup.MarginLayoutParams) mItemLayout.getLayoutParams();
             params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable()
                     : mController.getItemMarginEndDefault();
         }
 
-        void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
-                boolean showProgressBar, boolean showSubtitle, boolean showStatus,
-                boolean isFakeActive) {
-            setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle,
-                    showStatus, false, isFakeActive);
+        void setTwoLineLayout(CharSequence title, boolean showSeekBar,
+                boolean showProgressBar, boolean showSubtitle, boolean showStatus) {
+            setTwoLineLayout(title, showSeekBar, showProgressBar, showSubtitle, showStatus, false);
         }
 
-        void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
+        void setTwoLineLayout(CharSequence title,
                 boolean showSeekBar, boolean showProgressBar, boolean showSubtitle,
-                boolean showStatus , boolean showEndTouchArea, boolean isFakeActive) {
-            mTitleText.setVisibility(View.GONE);
-            mTwoLineLayout.setVisibility(View.VISIBLE);
+                boolean showStatus , boolean showEndTouchArea) {
             mStatusIcon.setVisibility(showStatus ? View.VISIBLE : View.GONE);
             mSeekBar.setAlpha(1);
             mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
             final Drawable backgroundDrawable;
             backgroundDrawable = mContext.getDrawable(
-                    showSeekBar || isFakeActive ? R.drawable.media_output_item_background_active
+                    showSeekBar ? R.drawable.media_output_item_background_active
                             : R.drawable.media_output_item_background).mutate();
             mItemLayout.setBackgroundTintList(ColorStateList.valueOf(
-                    showSeekBar || isFakeActive ? mController.getColorConnectedItemBackground()
+                    showSeekBar ? mController.getColorConnectedItemBackground()
                             : mController.getColorItemBackground()
             ));
-            mIconAreaLayout.setBackgroundTintList(
-                    ColorStateList.valueOf(showProgressBar || isFakeActive
-                            ? mController.getColorConnectedItemBackground()
-                            : showSeekBar ? mController.getColorSeekbarProgress()
-                                    : mController.getColorItemBackground()));
             if (showSeekBar) {
                 updateSeekbarProgressBackground();
             }
@@ -277,12 +254,7 @@
             mItemLayout.setBackground(backgroundDrawable);
             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
             mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
-            mTwoLineTitleText.setTranslationY(0);
-            mTwoLineTitleText.setText(device == null ? title : getItemTitle(device));
-            mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
-                            bFocused ? com.android.internal.R.string.config_headlineFontFamilyMedium
-                                    : com.android.internal.R.string.config_headlineFontFamily),
-                    Typeface.NORMAL));
+            mTitleText.setText(title);
         }
 
         void updateSeekbarProgressBackground() {
@@ -298,14 +270,8 @@
                             mController.getActiveRadius(), 0, 0});
         }
 
-        void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) {
-            if (!mController.isVolumeControlEnabled(device)) {
-                disableSeekBar();
-            } else {
-                enableSeekBar(device);
-            }
-            mSeekBar.setMaxVolume(device.getMaxVolume());
-            final int currentVolume = device.getCurrentVolume();
+        private void initializeSeekbarVolume(
+                MediaDevice device, int currentVolume, boolean isCurrentSeekbarInvisible) {
             if (!mIsDragging) {
                 if (mSeekBar.getVolume() != currentVolume && (mLatestUpdateVolume == -1
                         || currentVolume == mLatestUpdateVolume)) {
@@ -320,10 +286,7 @@
                         }
                     } else {
                         if (!mVolumeAnimator.isStarted()) {
-                            int percentage =
-                                    (int) ((double) currentVolume * VOLUME_PERCENTAGE_SCALE_SIZE
-                                            / (double) mSeekBar.getMax());
-                            if (percentage == 0) {
+                            if (currentVolume == 0) {
                                 updateMutedVolumeIcon(device);
                             } else {
                                 updateUnmutedVolumeIcon(device);
@@ -343,54 +306,75 @@
             if (mIsInitVolumeFirstTime) {
                 mIsInitVolumeFirstTime = false;
             }
-            mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
-                boolean mStartFromMute = false;
+        }
+
+        void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) {
+            SeekBarVolumeControl volumeControl = new SeekBarVolumeControl() {
                 @Override
-                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-                    if (device == null || !fromUser) {
-                        return;
-                    }
-                    int progressToVolume = MediaOutputSeekbar.scaleProgressToVolume(progress);
-                    int deviceVolume = device.getCurrentVolume();
-                    int percentage =
-                            (int) ((double) progressToVolume * VOLUME_PERCENTAGE_SCALE_SIZE
-                                    / (double) seekBar.getMax());
-                    mVolumeValueText.setText(mContext.getResources().getString(
-                            R.string.media_output_dialog_volume_percentage, percentage));
-                    if (mStartFromMute) {
-                        updateUnmutedVolumeIcon(device);
-                        mStartFromMute = false;
-                    }
-                    if (progressToVolume != deviceVolume) {
-                        mLatestUpdateVolume = progressToVolume;
-                        mController.adjustVolume(device, progressToVolume);
-                    }
+                public int getVolume() {
+                    return device.getCurrentVolume();
+                }
+                @Override
+                public void setVolume(int volume) {
+                    mController.adjustVolume(device, volume);
                 }
 
                 @Override
-                public void onStartTrackingTouch(SeekBar seekBar) {
-                    mTitleIcon.setVisibility(View.INVISIBLE);
-                    mVolumeValueText.setVisibility(View.VISIBLE);
-                    int currentVolume = MediaOutputSeekbar.scaleProgressToVolume(
-                            seekBar.getProgress());
-                    mStartFromMute = (currentVolume == 0);
-                    mIsDragging = true;
+                public void onMute() {
+                    mController.logInteractionUnmuteDevice(device);
                 }
+            };
 
+            if (!mController.isVolumeControlEnabled(device)) {
+                disableSeekBar();
+            } else {
+                enableSeekBar(volumeControl);
+            }
+            mSeekBar.setMaxVolume(device.getMaxVolume());
+            final int currentVolume = device.getCurrentVolume();
+            initializeSeekbarVolume(device, currentVolume, isCurrentSeekbarInvisible);
+
+            mSeekBar.setOnSeekBarChangeListener(new MediaSeekBarChangedListener(
+                    device, volumeControl) {
                 @Override
-                public void onStopTrackingTouch(SeekBar seekBar) {
-                    int currentVolume = MediaOutputSeekbar.scaleProgressToVolume(
-                            seekBar.getProgress());
-                    if (currentVolume == 0) {
-                        seekBar.setProgress(0);
-                        updateMutedVolumeIcon(device);
-                    } else {
-                        updateUnmutedVolumeIcon(device);
-                    }
-                    mTitleIcon.setVisibility(View.VISIBLE);
-                    mVolumeValueText.setVisibility(View.GONE);
+                public void onStopTrackingTouch(SeekBar seekbar) {
+                    super.onStopTrackingTouch(seekbar);
                     mController.logInteractionAdjustVolume(device);
-                    mIsDragging = false;
+                }
+            });
+        }
+
+        // Initializes the seekbar for a group of devices.
+        void initGroupSeekbar(boolean isCurrentSeekbarInvisible) {
+            SeekBarVolumeControl volumeControl = new SeekBarVolumeControl() {
+                @Override
+                public int getVolume() {
+                    return mController.getSessionVolume();
+                }
+
+                @Override
+                public void setVolume(int volume) {
+                    mController.adjustSessionVolume(volume);
+                }
+
+                @Override
+                public void onMute() {}
+            };
+
+            if (!mController.isVolumeControlEnabledForSession()) {
+                disableSeekBar();
+            } else {
+                enableSeekBar(volumeControl);
+            }
+            mSeekBar.setMaxVolume(mController.getSessionVolumeMax());
+
+            final int currentVolume = mController.getSessionVolume();
+            initializeSeekbarVolume(null, currentVolume, isCurrentSeekbarInvisible);
+            mSeekBar.setOnSeekBarChangeListener(new MediaSeekBarChangedListener(
+                    null, volumeControl) {
+                @Override
+                protected boolean shouldHandleProgressChanged() {
+                    return true;
                 }
             });
         }
@@ -421,7 +405,7 @@
         int getDrawableId(boolean isInputDevice, boolean isMutedVolumeIcon) {
             // Returns the microphone icon when the flag is enabled and the device is an input
             // device.
-            if (com.android.media.flags.Flags.enableAudioInputDeviceRoutingAndVolumeControl()
+            if (Flags.enableAudioInputDeviceRoutingAndVolumeControl()
                     && isInputDevice) {
                 return isMutedVolumeIcon ? R.drawable.ic_mic_off : R.drawable.ic_mic_26dp;
             }
@@ -443,8 +427,7 @@
             mItemLayout.setBackground(backgroundDrawable);
             mItemLayout.setBackgroundTintList(
                     ColorStateList.valueOf(mController.getColorConnectedItemBackground()));
-            mIconAreaLayout.setBackgroundTintList(
-                    ColorStateList.valueOf(mController.getColorConnectedItemBackground()));
+            mIconAreaLayout.setBackground(null);
         }
 
         private void initAnimator() {
@@ -489,27 +472,28 @@
             updateIconAreaClickListener(null);
         }
 
-        private void enableSeekBar(MediaDevice device) {
+        private void enableSeekBar(SeekBarVolumeControl volumeControl) {
             mSeekBar.setEnabled(true);
+
             mSeekBar.setOnTouchListener((v, event) -> false);
             updateIconAreaClickListener((v) -> {
-                if (device.getCurrentVolume() == 0) {
-                    mController.logInteractionUnmuteDevice(device);
+                if (volumeControl.getVolume() == 0) {
                     mSeekBar.setVolume(UNMUTE_DEFAULT_VOLUME);
-                    mController.adjustVolume(device, UNMUTE_DEFAULT_VOLUME);
-                    updateUnmutedVolumeIcon(device);
+                    volumeControl.setVolume(UNMUTE_DEFAULT_VOLUME);
+                    updateUnmutedVolumeIcon(null);
                     mIconAreaLayout.setOnTouchListener(((iconV, event) -> false));
                 } else {
-                    mController.logInteractionMuteDevice(device);
+                    volumeControl.onMute();
                     mSeekBar.resetVolume();
-                    mController.adjustVolume(device, 0);
-                    updateMutedVolumeIcon(device);
+                    volumeControl.setVolume(0);
+                    updateMutedVolumeIcon(null);
                     mIconAreaLayout.setOnTouchListener(((iconV, event) -> {
                         mSeekBar.dispatchTouchEvent(event);
                         return false;
                     }));
                 }
             });
+
         }
 
         protected void setUpDeviceIcon(MediaDevice device) {
@@ -525,5 +509,74 @@
                 });
             });
         }
+
+        interface SeekBarVolumeControl {
+            int getVolume();
+            void setVolume(int volume);
+            void onMute();
+        }
+
+        private abstract class MediaSeekBarChangedListener
+                implements SeekBar.OnSeekBarChangeListener {
+            boolean mStartFromMute = false;
+            private MediaDevice mMediaDevice;
+            private SeekBarVolumeControl mVolumeControl;
+
+            MediaSeekBarChangedListener(MediaDevice device, SeekBarVolumeControl volumeControl) {
+                mMediaDevice = device;
+                mVolumeControl = volumeControl;
+            }
+
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                if (!shouldHandleProgressChanged() || !fromUser) {
+                    return;
+                }
+
+                final String percentageString = mContext.getResources().getString(
+                        R.string.media_output_dialog_volume_percentage,
+                        mSeekBar.getPercentage());
+                mVolumeValueText.setText(percentageString);
+
+                if (mStartFromMute) {
+                    updateUnmutedVolumeIcon(mMediaDevice);
+                    mStartFromMute = false;
+                }
+
+                int seekBarVolume = MediaOutputSeekbar.scaleProgressToVolume(progress);
+                if (seekBarVolume != mVolumeControl.getVolume()) {
+                    mLatestUpdateVolume = seekBarVolume;
+                    mVolumeControl.setVolume(seekBarVolume);
+                }
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+                mTitleIcon.setVisibility(View.INVISIBLE);
+                mVolumeValueText.setVisibility(View.VISIBLE);
+                int currentVolume = MediaOutputSeekbar.scaleProgressToVolume(
+                        seekBar.getProgress());
+                mStartFromMute = (currentVolume == 0);
+                mIsDragging = true;
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                int currentVolume = MediaOutputSeekbar.scaleProgressToVolume(
+                        seekBar.getProgress());
+                if (currentVolume == 0) {
+                    seekBar.setProgress(0);
+                    updateMutedVolumeIcon(mMediaDevice);
+                } else {
+                    updateUnmutedVolumeIcon(mMediaDevice);
+                }
+                mTitleIcon.setVisibility(View.VISIBLE);
+                mVolumeValueText.setVisibility(View.GONE);
+                mIsDragging = false;
+            }
+            protected boolean shouldHandleProgressChanged() {
+                return mMediaDevice != null;
+            }
+        };
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java
index be5d607..b7381da 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java
@@ -16,22 +16,62 @@
 
 package com.android.systemui.media.dialog;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.widget.SeekBar;
 
+import com.android.systemui.res.R;
+
 /**
  * Customized SeekBar for MediaOutputDialog, apply scale between device volume and progress, to make
  * adjustment smoother.
  */
 public class MediaOutputSeekbar extends SeekBar {
+    // The scale is added to make slider value change smooth.
     private static final int SCALE_SIZE = 1000;
-    private static final int INITIAL_PROGRESS = 500;
-    public static final int VOLUME_PERCENTAGE_SCALE_SIZE = 100000;
+
+    @Nullable
+    private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener = null;
 
     public MediaOutputSeekbar(Context context, AttributeSet attrs) {
         super(context, attrs);
         setMin(0);
+        super.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                final String percentageString = context.getResources().getString(
+                        R.string.media_output_dialog_volume_percentage,
+                        getPercentage());
+                // Override the default TTS for the seekbar. The percentage should correspond to
+                // the volume value, not the progress value. I.e. for the volume range 0 - 25, the
+                // percentage should be 0%, 4%, 8%, etc. It should never be 6% since 6% doesn't map
+                // to an integer volume value.
+                setStateDescription(percentageString);
+                if (mOnSeekBarChangeListener != null) {
+                    mOnSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser);
+                }
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+                if (mOnSeekBarChangeListener != null) {
+                    mOnSeekBarChangeListener.onStartTrackingTouch(seekBar);
+                }
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                if (mOnSeekBarChangeListener != null) {
+                    mOnSeekBarChangeListener.onStopTrackingTouch(seekBar);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setOnSeekBarChangeListener(@Nullable SeekBar.OnSeekBarChangeListener listener) {
+        mOnSeekBarChangeListener = listener;
     }
 
     static int scaleProgressToVolume(int progress) {
@@ -39,11 +79,11 @@
     }
 
     static int scaleVolumeToProgress(int volume) {
-        return volume == 0 ? 0 : INITIAL_PROGRESS + volume * SCALE_SIZE;
+        return volume * SCALE_SIZE;
     }
 
     int getVolume() {
-        return getProgress() / SCALE_SIZE;
+        return scaleProgressToVolume(getProgress());
     }
 
     void setVolume(int volume) {
@@ -51,10 +91,18 @@
     }
 
     void setMaxVolume(int maxVolume) {
-        setMax(maxVolume * SCALE_SIZE);
+        setMax(scaleVolumeToProgress(maxVolume));
     }
 
     void resetVolume() {
         setProgress(getMin());
     }
+
+    int getPercentage() {
+        // The progress -> volume -> progress conversion is necessary to ensure that progress
+        // strictly corresponds to an integer volume value.
+        // Example: 10424 (progress) -> 10 (volume) -> 10000 (progress).
+        int normalizedProgress = scaleVolumeToProgress(scaleProgressToVolume(getProgress()));
+        return (int) ((double) normalizedProgress * 100 / getMax());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
index 15afd22..35c872f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
@@ -760,14 +760,26 @@
         if (connectedMediaDevice != null) {
             selectedDevicesIds.add(connectedMediaDevice.getId());
         }
+        boolean groupSelectedDevices =
+                com.android.media.flags.Flags.enableOutputSwitcherSessionGrouping();
+        int nextSelectedItemIndex = 0;
         boolean suggestedDeviceAdded = false;
         boolean displayGroupAdded = false;
+        boolean selectedDeviceAdded = false;
         for (MediaDevice device : devices) {
             if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) {
                 finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device));
+                nextSelectedItemIndex++;
             } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains(
                     device.getId())) {
-                finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device));
+                if (groupSelectedDevices) {
+                    finalMediaItems.add(
+                            nextSelectedItemIndex++,
+                            MediaItem.createDeviceMediaItem(device, !selectedDeviceAdded));
+                    selectedDeviceAdded = true;
+                } else {
+                    finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device));
+                }
             } else {
                 if (device.isSuggestedDevice() && !suggestedDeviceAdded) {
                     addSuggestedDeviceGroupDivider(finalMediaItems);
@@ -1331,6 +1343,10 @@
         return !device.isVolumeFixed();
     }
 
+    boolean isVolumeControlEnabledForSession() {
+        return mLocalMediaManager.isMediaSessionAvailableForVolumeControl();
+    }
+
     private void startActivity(Intent intent, ActivityTransitionAnimator.Controller controller) {
         // Media Output dialog can be shown from the volume panel. This makes sure the panel is
         // closed when navigating to another activity, so it doesn't stays on top of it
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
index 7d30948..d503fb7 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager.RunningTaskInfo
 import android.hardware.display.DisplayManager
+import android.media.projection.MediaProjectionEvent
 import android.media.projection.MediaProjectionInfo
 import android.media.projection.MediaProjectionManager
 import android.media.projection.StopReason
@@ -40,16 +41,17 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.withContext
 
 @SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
 class MediaProjectionManagerRepository
 @Inject
 constructor(
@@ -85,48 +87,59 @@
         }
     }
 
-    override val mediaProjectionState: Flow<MediaProjectionState> =
-        conflatedCallbackFlow {
-                val callback =
-                    object : MediaProjectionManager.Callback() {
-                        override fun onStart(info: MediaProjectionInfo?) {
-                            logger.log(
-                                TAG,
-                                LogLevel.DEBUG,
-                                {},
-                                { "MediaProjectionManager.Callback#onStart" },
-                            )
-                            trySendWithFailureLogging(CallbackEvent.OnStart(info), TAG)
-                        }
+    private val callbackEventsFlow = conflatedCallbackFlow {
+        val callback =
+            object : MediaProjectionManager.Callback() {
+                override fun onStart(info: MediaProjectionInfo?) {
+                    logger.log(TAG, LogLevel.DEBUG, {}, { "Callback#onStart" })
+                    trySendWithFailureLogging(CallbackEvent.OnStart(info), TAG)
+                }
 
-                        override fun onStop(info: MediaProjectionInfo?) {
-                            logger.log(
-                                TAG,
-                                LogLevel.DEBUG,
-                                {},
-                                { "MediaProjectionManager.Callback#onStop" },
-                            )
-                            trySendWithFailureLogging(CallbackEvent.OnStop, TAG)
-                        }
+                override fun onStop(info: MediaProjectionInfo?) {
+                    logger.log(TAG, LogLevel.DEBUG, {}, { "Callback#onStop" })
+                    trySendWithFailureLogging(CallbackEvent.OnStop, TAG)
+                }
 
-                        override fun onRecordingSessionSet(
-                            info: MediaProjectionInfo,
-                            session: ContentRecordingSession?,
-                        ) {
-                            logger.log(
-                                TAG,
-                                LogLevel.DEBUG,
-                                { str1 = session.toString() },
-                                { "MediaProjectionManager.Callback#onSessionStarted: $str1" },
-                            )
-                            trySendWithFailureLogging(
-                                CallbackEvent.OnRecordingSessionSet(info, session),
-                                TAG,
-                            )
-                        }
+                override fun onRecordingSessionSet(
+                    info: MediaProjectionInfo,
+                    session: ContentRecordingSession?,
+                ) {
+                    logger.log(
+                        TAG,
+                        LogLevel.DEBUG,
+                        { str1 = session.toString() },
+                        { "Callback#onSessionSet: $str1" },
+                    )
+                    trySendWithFailureLogging(
+                        CallbackEvent.OnRecordingSessionSet(info, session),
+                        TAG,
+                    )
+                }
+
+                override fun onMediaProjectionEvent(
+                    event: MediaProjectionEvent,
+                    info: MediaProjectionInfo?,
+                    session: ContentRecordingSession?,
+                ) {
+                    if (com.android.media.projection.flags.Flags.showStopDialogPostCallEnd()) {
+                        logger.log(
+                            TAG,
+                            LogLevel.DEBUG,
+                            { str1 = event.toString() },
+                            { "Callback#onMediaProjectionEvent : $str1" },
+                        )
+                        trySendWithFailureLogging(CallbackEvent.OnMediaProjectionEvent(event), TAG)
                     }
-                mediaProjectionManager.addCallback(callback, handler)
-                awaitClose { mediaProjectionManager.removeCallback(callback) }
+                }
+            }
+        mediaProjectionManager.addCallback(callback, handler)
+        awaitClose { mediaProjectionManager.removeCallback(callback) }
+    }
+
+    override val mediaProjectionState: Flow<MediaProjectionState> =
+        callbackEventsFlow
+            .filterNot {
+                it is CallbackEvent.OnMediaProjectionEvent // Exclude OnMediaProjectionEvent
             }
             // When we get an #onRecordingSessionSet event, we need to do some work in the
             // background before emitting the right state value. But when we get an #onStop
@@ -161,6 +174,11 @@
                     }
                     is CallbackEvent.OnStop -> MediaProjectionState.NotProjecting
                     is CallbackEvent.OnRecordingSessionSet -> stateForSession(it.info, it.session)
+                    is CallbackEvent.OnMediaProjectionEvent ->
+                        throw IllegalStateException(
+                            "Unexpected OnMediaProjectionEvent in mediaProjectionState flow. It " +
+                                "should have been filtered out."
+                        )
                 }
             }
             .stateIn(
@@ -169,6 +187,16 @@
                 initialValue = MediaProjectionState.NotProjecting,
             )
 
+    override val projectionStartedDuringCallAndActivePostCallEvent: Flow<Unit> =
+        callbackEventsFlow
+            .filter {
+                com.android.media.projection.flags.Flags.showStopDialogPostCallEnd() &&
+                    it is CallbackEvent.OnMediaProjectionEvent &&
+                    it.event.eventType ==
+                        MediaProjectionEvent.PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL
+            }
+            .map {}
+
     private suspend fun stateForSession(
         info: MediaProjectionInfo,
         session: ContentRecordingSession?,
@@ -208,6 +236,8 @@
             val info: MediaProjectionInfo,
             val session: ContentRecordingSession?,
         ) : CallbackEvent
+
+        data class OnMediaProjectionEvent(val event: MediaProjectionEvent) : CallbackEvent
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
index a01d8c2..826ee58 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
@@ -32,4 +32,10 @@
 
     /** Represents the current [MediaProjectionState]. */
     val mediaProjectionState: Flow<MediaProjectionState>
+
+    /**
+     * Emits each time a call ends but media projection is still active and media projection was
+     * starting during the call.
+     */
+    val projectionStartedDuringCallAndActivePostCallEvent: Flow<Unit>
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
index ccc54f1..dca0f62 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
@@ -26,14 +26,12 @@
 import com.android.systemui.mediaprojection.taskswitcher.data.repository.TasksRepository
 import com.android.systemui.mediaprojection.taskswitcher.domain.model.TaskSwitchState
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 
 /** Interactor with logic related to task switching in the context of media projection. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class TaskSwitchInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 67fe0e9..1a5e605 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -79,7 +79,8 @@
 
     /** Methods to this call can be chained together before calling {@link #commitUpdate(int)}. */
     public SysUiState setFlag(@SystemUiStateFlags long flag, boolean enabled) {
-        final Boolean overrideOrNull = mSceneContainerPlugin.flagValueOverride(flag);
+        final Boolean overrideOrNull = mSceneContainerPlugin != null
+                ? mSceneContainerPlugin.flagValueOverride(flag) : null;
         if (overrideOrNull != null && enabled != overrideOrNull) {
             if (DEBUG) {
                 Log.d(TAG, "setFlag for flag " + flag + " and value " + enabled + " overridden to "
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 173a964..1807847 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -77,7 +77,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.Flags;
@@ -115,7 +115,7 @@
         AccessibilityButtonModeObserver.ModeChangedListener,
         AccessibilityButtonTargetsObserver.TargetsChangedListener,
         AccessibilityGestureTargetsObserver.TargetsChangedListener,
-        OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+        LauncherProxyService.LauncherProxyListener, NavigationModeController.ModeChangedListener,
         Dumpable, CommandQueue.Callbacks, ConfigurationController.ConfigurationListener {
     private static final String TAG = NavBarHelper.class.getSimpleName();
 
@@ -199,7 +199,7 @@
             AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
             AccessibilityGestureTargetsObserver accessibilityGestureTargetsObserver,
             SystemActions systemActions,
-            OverviewProxyService overviewProxyService,
+            LauncherProxyService launcherProxyService,
             Lazy<AssistManager> assistManagerLazy,
             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
             KeyguardStateController keyguardStateController,
@@ -240,7 +240,7 @@
         mNavBarMode = navigationModeController.addListener(this);
         mCommandQueue.addCallback(this);
         configurationController.addCallback(this);
-        overviewProxyService.addCallback(this);
+        launcherProxyService.addCallback(this);
         dumpManager.registerDumpable(this);
     }
 
@@ -536,10 +536,12 @@
     }
 
     /**
-     * @return Whether the IME is shown on top of the screen given the {@code vis} flag of
-     * {@link InputMethodService} and the keyguard states.
+     * Checks whether the IME is visible on top of the screen, based on the given IME window
+     * visibility flags, and the current keyguard state.
+     *
+     * @param vis the IME window visibility.
      */
-    public boolean isImeShown(@ImeWindowVisibility int vis) {
+    public boolean isImeVisible(@ImeWindowVisibility int vis) {
         View shadeWindowView =  mNotificationShadeWindowController.getWindowRootView();
         boolean isKeyguardShowing = mKeyguardStateController.isShowing();
         boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 645bd0b..babb640 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -52,7 +52,7 @@
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.views.NavigationBar;
 import com.android.systemui.navigationbar.views.NavigationBarView;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -115,7 +115,7 @@
 
     @Inject
     public NavigationBarControllerImpl(Context context,
-            OverviewProxyService overviewProxyService,
+            LauncherProxyService launcherProxyService,
             NavigationModeController navigationModeController,
             SysUiState sysUiFlagsContainer,
             CommandQueue commandQueue,
@@ -145,7 +145,7 @@
         mNavMode = navigationModeController.addListener(this);
         mNavBarHelper = navBarHelper;
         mTaskbarDelegate = taskbarDelegate;
-        mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
+        mTaskbarDelegate.setDependencies(commandQueue, launcherProxyService,
                 navBarHelper, navigationModeController, sysUiFlagsContainer,
                 dumpManager, autoHideControllerStore.forDisplay(mContext.getDisplayId()),
                 lightBarController, pipOptional, backAnimation.orElse(null),
@@ -284,7 +284,10 @@
         }
 
         @Override
-        public void onDisplayReady(int displayId) {
+        public void onDisplayAddSystemDecorations(int displayId) {
+            if (enableDisplayContentModeManagement()) {
+                mHasNavBar.put(displayId, true);
+            }
             Display display = mDisplayManager.getDisplay(displayId);
             mIsLargeScreen = isLargeScreen(mContext);
             createNavigationBar(display, null /* savedState */, null /* result */);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 05d8bff..c4d847f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -17,8 +17,9 @@
 package com.android.systemui.navigationbar;
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.app.StatusBarManager.NAVBAR_BACK_DISMISS_IME;
+import static android.app.StatusBarManager.NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
+import static android.app.StatusBarManager.NAVBAR_IME_VISIBLE;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -29,14 +30,16 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISMISS_IME;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
 import android.app.StatusBarManager;
+import android.app.StatusBarManager.NavbarFlags;
 import android.app.StatusBarManager.WindowVisibleState;
 import android.content.Context;
 import android.graphics.Rect;
@@ -67,7 +70,7 @@
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.statusbar.phone.BarTransitions;
@@ -93,7 +96,7 @@
 /** */
 @SysUISingleton
 public class TaskbarDelegate implements CommandQueue.Callbacks,
-        OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+        LauncherProxyService.LauncherProxyListener, NavigationModeController.ModeChangedListener,
         Dumpable {
     private static final String TAG = TaskbarDelegate.class.getSimpleName();
 
@@ -101,7 +104,7 @@
     private final LightBarTransitionsController.Factory mLightBarTransitionsControllerFactory;
     private boolean mInitialized;
     private CommandQueue mCommandQueue;
-    private OverviewProxyService mOverviewProxyService;
+    private LauncherProxyService mLauncherProxyService;
     private NavBarHelper mNavBarHelper;
     private NavigationModeController mNavigationModeController;
     private SysUiState mSysUiState;
@@ -111,7 +114,8 @@
     private TaskStackChangeListeners mTaskStackChangeListeners;
     private Optional<Pip> mPipOptional;
     private int mDefaultDisplayId;
-    private int mNavigationIconHints;
+    @NavbarFlags
+    private int mNavbarFlags;
     private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
             new NavBarHelper.NavbarTaskbarStateUpdater() {
                 @Override
@@ -206,7 +210,7 @@
     }
 
     public void setDependencies(CommandQueue commandQueue,
-            OverviewProxyService overviewProxyService,
+            LauncherProxyService launcherProxyService,
             NavBarHelper navBarHelper,
             NavigationModeController navigationModeController,
             SysUiState sysUiState, DumpManager dumpManager,
@@ -218,7 +222,7 @@
             DisplayTracker displayTracker) {
         // TODO: adding this in the ctor results in a dagger dependency cycle :(
         mCommandQueue = commandQueue;
-        mOverviewProxyService = overviewProxyService;
+        mLauncherProxyService = launcherProxyService;
         mNavBarHelper = navBarHelper;
         mNavigationModeController = navigationModeController;
         mSysUiState = sysUiState;
@@ -234,33 +238,47 @@
     }
 
     @Override
-    public void onDisplayReady(int displayId) {
-        CommandQueue.Callbacks.super.onDisplayReady(displayId);
-        if (mOverviewProxyService.getProxy() == null) {
+    public void onDisplayAddSystemDecorations(int displayId) {
+        CommandQueue.Callbacks.super.onDisplayAddSystemDecorations(displayId);
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
         try {
-            mOverviewProxyService.getProxy().onDisplayReady(displayId);
+            mLauncherProxyService.getProxy().onDisplayAddSystemDecorations(displayId);
         } catch (RemoteException e) {
-            Log.e(TAG, "onDisplayReady() failed", e);
+            Log.e(TAG, "onDisplayAddSystemDecorations() failed", e);
         }
     }
 
     @Override
     public void onDisplayRemoved(int displayId) {
         CommandQueue.Callbacks.super.onDisplayRemoved(displayId);
-        if (mOverviewProxyService.getProxy() == null) {
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
         try {
-            mOverviewProxyService.getProxy().onDisplayRemoved(displayId);
+            mLauncherProxyService.getProxy().onDisplayRemoved(displayId);
         } catch (RemoteException e) {
             Log.e(TAG, "onDisplayRemoved() failed", e);
         }
     }
 
+    @Override
+    public void onDisplayRemoveSystemDecorations(int displayId) {
+        CommandQueue.Callbacks.super.onDisplayRemoveSystemDecorations(displayId);
+        if (mLauncherProxyService.getProxy() == null) {
+            return;
+        }
+
+        try {
+            mLauncherProxyService.getProxy().onDisplayRemoveSystemDecorations(displayId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "onDisplaySystemDecorationsRemoved() failed", e);
+        }
+    }
+
     // Separated into a method to keep setDependencies() clean/readable.
     private LightBarTransitionsController createLightBarTransitionsController() {
 
@@ -269,7 +287,7 @@
                     @Override
                     public void applyDarkIntensity(float darkIntensity) {
                         mBgHandler.post(() -> {
-                            mOverviewProxyService.onNavButtonsDarkIntensityChanged(darkIntensity);
+                            mLauncherProxyService.onNavButtonsDarkIntensityChanged(darkIntensity);
                         });
                     }
 
@@ -291,7 +309,7 @@
             mDefaultDisplayId = displayId;
             parseCurrentSysuiState();
             mCommandQueue.addCallback(this);
-            mOverviewProxyService.addCallback(this);
+            mLauncherProxyService.addCallback(this);
             onNavigationModeChanged(mNavigationModeController.addListener(this));
             mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
             // Initialize component callback
@@ -316,7 +334,7 @@
             return;
         }
         mCommandQueue.removeCallback(this);
-        mOverviewProxyService.removeCallback(this);
+        mLauncherProxyService.removeCallback(this);
         mNavigationModeController.removeListener(this);
         mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
         mScreenPinningNotify = null;
@@ -360,10 +378,12 @@
 
         mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
                 .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
-                .setFlag(SYSUI_STATE_IME_SHOWING,
-                        (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
-                .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
-                        (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
+                .setFlag(SYSUI_STATE_IME_VISIBLE,
+                        (mNavbarFlags & NAVBAR_IME_VISIBLE) != 0)
+                .setFlag(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE,
+                        (mNavbarFlags & NAVBAR_IME_SWITCHER_BUTTON_VISIBLE) != 0)
+                .setFlag(SYSUI_STATE_BACK_DISMISS_IME,
+                        (mNavbarFlags & NAVBAR_BACK_DISMISS_IME) != 0)
                 .setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
                         (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
                 .setFlag(SYSUI_STATE_HOME_DISABLED,
@@ -381,43 +401,43 @@
     }
 
     void onTransitionModeUpdated(int barMode, boolean checkBarModes) {
-        if (mOverviewProxyService.getProxy() == null) {
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
         try {
-            mOverviewProxyService.getProxy().onTransitionModeUpdated(barMode, checkBarModes);
+            mLauncherProxyService.getProxy().onTransitionModeUpdated(barMode, checkBarModes);
         } catch (RemoteException e) {
             Log.e(TAG, "onTransitionModeUpdated() failed, barMode: " + barMode, e);
         }
     }
 
     void checkNavBarModes(int displayId) {
-        if (mOverviewProxyService.getProxy() == null) {
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
         try {
-            mOverviewProxyService.getProxy().checkNavBarModes(displayId);
+            mLauncherProxyService.getProxy().checkNavBarModes(displayId);
         } catch (RemoteException e) {
             Log.e(TAG, "checkNavBarModes() failed", e);
         }
     }
 
     void finishBarAnimations(int displayId) {
-        if (mOverviewProxyService.getProxy() == null) {
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
         try {
-            mOverviewProxyService.getProxy().finishBarAnimations(displayId);
+            mLauncherProxyService.getProxy().finishBarAnimations(displayId);
         } catch (RemoteException e) {
             Log.e(TAG, "finishBarAnimations() failed", e);
         }
     }
 
     void touchAutoDim(int displayId) {
-        if (mOverviewProxyService.getProxy() == null) {
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
@@ -425,31 +445,31 @@
             int state = mStatusBarStateController.getState();
             boolean shouldReset =
                     state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED;
-            mOverviewProxyService.getProxy().touchAutoDim(displayId, shouldReset);
+            mLauncherProxyService.getProxy().touchAutoDim(displayId, shouldReset);
         } catch (RemoteException e) {
             Log.e(TAG, "touchAutoDim() failed", e);
         }
     }
 
     void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode, boolean animate) {
-        if (mOverviewProxyService.getProxy() == null) {
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
         try {
-            mOverviewProxyService.getProxy().transitionTo(displayId, barMode, animate);
+            mLauncherProxyService.getProxy().transitionTo(displayId, barMode, animate);
         } catch (RemoteException e) {
             Log.e(TAG, "transitionTo() failed, barMode: " + barMode, e);
         }
     }
     private void updateAssistantAvailability(boolean assistantAvailable,
             boolean longPressHomeEnabled) {
-        if (mOverviewProxyService.getProxy() == null) {
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
         try {
-            mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable,
+            mLauncherProxyService.getProxy().onAssistantAvailable(assistantAvailable,
                     longPressHomeEnabled);
         } catch (RemoteException e) {
             Log.e(TAG, "onAssistantAvailable() failed, available: " + assistantAvailable, e);
@@ -457,24 +477,24 @@
     }
 
     private void updateWallpaperVisible(int displayId, boolean visible) {
-        if (mOverviewProxyService.getProxy() == null) {
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
         try {
-            mOverviewProxyService.getProxy().updateWallpaperVisibility(displayId, visible);
+            mLauncherProxyService.getProxy().updateWallpaperVisibility(displayId, visible);
         } catch (RemoteException e) {
             Log.e(TAG, "updateWallpaperVisibility() failed, visible: " + visible, e);
         }
     }
 
     private void appTransitionPending(boolean pending) {
-        if (mOverviewProxyService.getProxy() == null) {
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
         try {
-            mOverviewProxyService.getProxy().appTransitionPending(pending);
+            mLauncherProxyService.getProxy().appTransitionPending(pending);
         } catch (RemoteException e) {
             Log.e(TAG, "appTransitionPending() failed, pending: " + pending, e);
         }
@@ -483,18 +503,17 @@
     @Override
     public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
             @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
-        boolean imeShown = mNavBarHelper.isImeShown(vis);
-        if (!imeShown) {
-            // Count imperceptible changes as visible so we transition taskbar out quickly.
-            imeShown = (vis & InputMethodService.IME_VISIBLE_IMPERCEPTIBLE) != 0;
+        // Count imperceptible changes as visible so we transition taskbar out quickly.
+        final boolean isImeVisible = mNavBarHelper.isImeVisible(vis)
+                || (vis & InputMethodService.IME_VISIBLE_IMPERCEPTIBLE) != 0;
+        final int flags = Utilities.updateNavbarFlagsFromIme(mNavbarFlags, backDisposition,
+                isImeVisible, showImeSwitcher);
+        if (flags == mNavbarFlags) {
+            return;
         }
-        showImeSwitcher = imeShown && showImeSwitcher;
-        int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
-                imeShown, showImeSwitcher);
-        if (hints != mNavigationIconHints) {
-            mNavigationIconHints = hints;
-            updateSysuiFlags();
-        }
+
+        mNavbarFlags = flags;
+        updateSysuiFlags();
     }
 
     @Override
@@ -509,14 +528,14 @@
 
     @Override
     public void onRotationProposal(int rotation, boolean isValid) {
-        mOverviewProxyService.onRotationProposal(rotation, isValid);
+        mLauncherProxyService.onRotationProposal(rotation, isValid);
     }
 
     @Override
     public void disable(int displayId, int state1, int state2, boolean animate) {
         mDisabledFlags = state1;
         updateSysuiFlags();
-        mOverviewProxyService.disable(displayId, state1, state2, animate);
+        mLauncherProxyService.disable(displayId, state1, state2, animate);
     }
 
     @Override
@@ -524,7 +543,7 @@
             AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
             @InsetsType int requestedVisibleTypes, String packageName,
             LetterboxDetails[] letterboxDetails) {
-        mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
+        mLauncherProxyService.onSystemBarAttributesChanged(displayId, behavior);
         boolean nbModeChanged = false;
         if (mAppearance != appearance) {
             mAppearance = appearance;
@@ -577,12 +596,12 @@
 
     @Override
     public void toggleTaskbar() {
-        if (mOverviewProxyService.getProxy() == null) {
+        if (mLauncherProxyService.getProxy() == null) {
             return;
         }
 
         try {
-            mOverviewProxyService.getProxy().onTaskbarToggled();
+            mLauncherProxyService.getProxy().onTaskbarToggled();
         } catch (RemoteException e) {
             Log.e(TAG, "onTaskbarToggled() failed", e);
         }
@@ -654,7 +673,7 @@
 
     @Override
     public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
-        mOverviewProxyService.onNavigationBarLumaSamplingEnabled(displayId, enable);
+        mLauncherProxyService.onNavigationBarLumaSamplingEnabled(displayId, enable);
     }
 
     @Override
@@ -688,7 +707,7 @@
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("TaskbarDelegate (mDefaultDisplayId=" + mDefaultDisplayId + "):");
-        pw.println("  mNavigationIconHints=" + mNavigationIconHints);
+        pw.println("  mNavbarFlags=" + mNavbarFlags);
         pw.println("  mNavigationMode=" + mNavigationMode);
         pw.println("  mDisabledFlags=" + mDisabledFlags);
         pw.println("  mTaskBarWindowState=" + mTaskBarWindowState);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 1c94f56..f44c2c0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -85,7 +85,7 @@
 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -155,8 +155,8 @@
                 }
             };
 
-    private OverviewProxyService.OverviewProxyListener mQuickSwitchListener =
-            new OverviewProxyService.OverviewProxyListener() {
+    private LauncherProxyService.LauncherProxyListener mQuickSwitchListener =
+            new LauncherProxyService.LauncherProxyListener() {
                 @Override
                 public void onPrioritizedRotation(@Surface.Rotation int rotation) {
                     mStartingQuickstepRotation = rotation;
@@ -197,7 +197,7 @@
 
     private final Context mContext;
     private final UserTracker mUserTracker;
-    private final OverviewProxyService mOverviewProxyService;
+    private final LauncherProxyService mLauncherProxyService;
     private final SysUiState mSysUiState;
     private Runnable mStateChangeCallback;
     private Consumer<Boolean> mButtonForcedVisibleCallback;
@@ -332,7 +332,7 @@
                             : SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED);
                     if (!mInRejectedExclusion) {
                         // Log successful back gesture to contextual edu stats
-                        mOverviewProxyService.updateContextualEduStats(mIsTrackpadThreeFingerSwipe,
+                        mLauncherProxyService.updateContextualEduStats(mIsTrackpadThreeFingerSwipe,
                                 GestureType.BACK);
                     }
                 }
@@ -441,7 +441,7 @@
     @AssistedInject
     EdgeBackGestureHandler(
             @Assisted Context context,
-            OverviewProxyService overviewProxyService,
+            LauncherProxyService launcherProxyService,
             SysUiState sysUiState,
             PluginManager pluginManager,
             @BackPanelUiThread UiThreadContext uiThreadContext,
@@ -468,7 +468,7 @@
         mBackgroundExecutor = backgroundExecutor;
         mBgHandler = bgHandler;
         mUserTracker = userTracker;
-        mOverviewProxyService = overviewProxyService;
+        mLauncherProxyService = launcherProxyService;
         mSysUiState = sysUiState;
         mPluginManager = pluginManager;
         mNavigationModeController = navigationModeController;
@@ -620,7 +620,7 @@
      */
     public void onNavBarAttached() {
         mIsAttached = true;
-        mOverviewProxyService.addCallback(mQuickSwitchListener);
+        mLauncherProxyService.addCallback(mQuickSwitchListener);
         mSysUiState.addCallback(mSysUiStateCallback);
         mInputManager.registerInputDeviceListener(mInputDeviceListener, mBgHandler);
         int[] inputDevices = mInputManager.getInputDeviceIds();
@@ -636,7 +636,7 @@
      */
     public void onNavBarDetached() {
         mIsAttached = false;
-        mOverviewProxyService.removeCallback(mQuickSwitchListener);
+        mLauncherProxyService.removeCallback(mQuickSwitchListener);
         mSysUiState.removeCallback(mSysUiStateCallback);
         mInputManager.unregisterInputDeviceListener(mInputDeviceListener);
         mTrackpadsConnected.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index c787507..f95f459 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -17,12 +17,14 @@
 package com.android.systemui.navigationbar.views;
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.app.StatusBarManager.NAVBAR_BACK_DISMISS_IME;
+import static android.app.StatusBarManager.NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
+import static android.app.StatusBarManager.NAVBAR_IME_VISIBLE;
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.WindowType;
 import static android.app.StatusBarManager.WindowVisibleState;
+import static android.app.StatusBarManager.navbarFlagsToString;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.view.InsetsSource.FLAG_SUPPRESS_SCRIM;
@@ -34,7 +36,7 @@
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
 import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
-import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.recents.LauncherProxyService.LauncherProxyListener;
 import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
 import static com.android.systemui.shared.rotation.RotationButtonController.DEBUG_ROTATION;
 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -42,8 +44,9 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISMISS_IME;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
@@ -56,6 +59,7 @@
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.app.StatusBarManager;
+import android.app.StatusBarManager.NavbarFlags;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Insets;
@@ -127,7 +131,7 @@
 import com.android.systemui.navigationbar.views.buttons.NavBarButtonClickLogger;
 import com.android.systemui.navigationbar.views.buttons.NavbarOrientationTrackingLogger;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.DisplayTracker;
@@ -208,7 +212,7 @@
     private final ShadeViewController mShadeViewController;
     private final PanelExpansionInteractor mPanelExpansionInteractor;
     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
-    private final OverviewProxyService mOverviewProxyService;
+    private final LauncherProxyService mLauncherProxyService;
     private final NavigationModeController mNavigationModeController;
     private final UserTracker mUserTracker;
     private final CommandQueue mCommandQueue;
@@ -233,7 +237,8 @@
 
     private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
 
-    private int mNavigationIconHints = 0;
+    @NavbarFlags
+    private int mNavbarFlags;
     private @TransitionMode int mTransitionMode;
     private boolean mLongPressHomeEnabled;
 
@@ -278,7 +283,7 @@
      * gesture to indicate to them that they can continue in that orientation without having to
      * rotate the phone
      * The secondary handle will show when we get
-     * {@link OverviewProxyListener#notifyPrioritizedRotation(int)} callback with the
+     * {@link LauncherProxyListener#notifyPrioritizedRotation(int)} callback with the
      * original handle hidden and we'll flip the visibilities once the
      * {@link #mTasksFrozenListener} fires
      */
@@ -382,12 +387,12 @@
                 }
             };
 
-    private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
+    private final LauncherProxyListener mLauncherProxyListener = new LauncherProxyListener() {
         @Override
         public void onConnectionChanged(boolean isConnected) {
-            mView.onOverviewProxyConnectionChange(
-                    mOverviewProxyService.isEnabled());
-            mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI());
+            mView.onLauncherProxyConnectionChange(
+                    mLauncherProxyService.isEnabled());
+            mView.setShouldShowSwipeUpUi(mLauncherProxyService.shouldShowSwipeUpUI());
             updateScreenPinningGestures();
         }
 
@@ -560,7 +565,7 @@
             AccessibilityManager accessibilityManager,
             DeviceProvisionedController deviceProvisionedController,
             MetricsLogger metricsLogger,
-            OverviewProxyService overviewProxyService,
+            LauncherProxyService launcherProxyService,
             NavigationModeController navigationModeController,
             StatusBarStateController statusBarStateController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -613,7 +618,7 @@
         mShadeViewController = shadeViewController;
         mPanelExpansionInteractor = panelExpansionInteractor;
         mNotificationRemoteInputManager = notificationRemoteInputManager;
-        mOverviewProxyService = overviewProxyService;
+        mLauncherProxyService = launcherProxyService;
         mNavigationModeController = navigationModeController;
         mUserTracker = userTracker;
         mCommandQueue = commandQueue;
@@ -649,18 +654,18 @@
             if (!mEdgeBackGestureHandler.isHandlingGestures()) {
                 // We're in 2/3 button mode OR back button force-shown in SUW
                 if (!mImeVisible) {
-                    // IME not showing, take all touches
+                    // IME is not visible, take all touches
                     info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
                     return;
                 }
                 if (!mView.isImeRenderingNavButtons()) {
-                    // IME showing but not drawing any buttons, take all touches
+                    // IME is visible but not drawing any buttons, take all touches
                     info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
                     return;
                 }
             }
 
-            // When in gestural and the IME is showing, don't use the nearest region since it will
+            // When in gestural and the IME is visible, don't use the nearest region since it will
             // take gesture space away from the IME
             info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
             info.touchableRegion.set(
@@ -817,13 +822,12 @@
         if (mSavedState != null) {
             getBarTransitions().getLightTransitionsController().restoreState(mSavedState);
         }
-        setNavigationIconHints(mNavigationIconHints);
         setWindowVisible(isNavBarWindowVisible());
         mView.setBehavior(mBehavior);
         setNavBarMode(mNavBarMode);
         repositionNavigationBar(mCurrentRotation);
         mView.setUpdateActiveTouchRegionsCallback(
-                () -> mOverviewProxyService.onActiveNavBarRegionChanges(
+                () -> mLauncherProxyService.onActiveNavBarRegionChanges(
                         getButtonLocations(true /* inScreen */, true /* useNearestRegion */)));
 
         mView.getViewTreeObserver().addOnComputeInternalInsetsListener(
@@ -839,7 +843,7 @@
         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
         notifyNavigationBarScreenOn();
 
-        mOverviewProxyService.addCallback(mOverviewProxyListener);
+        mLauncherProxyService.addCallback(mLauncherProxyListener);
         updateSystemUiStateFlags();
 
         // Currently there is no accelerometer sensor on non-default display.
@@ -877,7 +881,7 @@
     public void onViewDetached() {
         mView.setUpdateActiveTouchRegionsCallback(null);
         getBarTransitions().destroy();
-        mOverviewProxyService.removeCallback(mOverviewProxyListener);
+        mLauncherProxyService.removeCallback(mLauncherProxyListener);
         mUserTracker.removeCallback(mUserChangedCallback);
         mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
         if (mOrientationHandle != null) {
@@ -1111,6 +1115,7 @@
         pw.println("  mLongPressHomeEnabled=" + mLongPressHomeEnabled);
         pw.println("  mNavigationBarWindowState="
                 + windowStateToString(mNavigationBarWindowState));
+        pw.println("  mNavbarFlags=" + navbarFlagsToString(mNavbarFlags));
         pw.println("  mTransitionMode="
                 + BarTransitions.modeToString(mTransitionMode));
         pw.println("  mTransientShown=" + mTransientShown);
@@ -1135,13 +1140,14 @@
         if (displayId != mDisplayId) {
             return;
         }
-        boolean imeShown = mNavBarHelper.isImeShown(vis);
-        showImeSwitcher = imeShown && showImeSwitcher;
-        int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
-                imeShown, showImeSwitcher);
-        if (hints == mNavigationIconHints) return;
+        final boolean isImeVisible = mNavBarHelper.isImeVisible(vis);
+        final int flags = Utilities.updateNavbarFlagsFromIme(mNavbarFlags, backDisposition,
+                isImeVisible, showImeSwitcher);
+        if (flags == mNavbarFlags) {
+            return;
+        }
 
-        setNavigationIconHints(hints);
+        setNavbarFlags(flags);
         checkBarModes();
         updateSystemUiStateFlags();
     }
@@ -1680,10 +1686,12 @@
         mSysUiFlagsContainer.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
                 .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
                 .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
-                .setFlag(SYSUI_STATE_IME_SHOWING,
-                        (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
-                .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
-                        (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
+                .setFlag(SYSUI_STATE_IME_VISIBLE,
+                        (mNavbarFlags & NAVBAR_IME_VISIBLE) != 0)
+                .setFlag(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE,
+                        (mNavbarFlags & NAVBAR_IME_SWITCHER_BUTTON_VISIBLE) != 0)
+                .setFlag(SYSUI_STATE_BACK_DISMISS_IME,
+                        (mNavbarFlags & NAVBAR_BACK_DISMISS_IME) != 0)
                 .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
                         allowSystemGestureIgnoringBarVisibility())
                 .commitUpdate(mDisplayId);
@@ -1691,9 +1699,9 @@
 
     private void updateAssistantEntrypoints(boolean assistantAvailable,
             boolean longPressHomeEnabled) {
-        if (mOverviewProxyService.getProxy() != null) {
+        if (mLauncherProxyService.getProxy() != null) {
             try {
-                mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable,
+                mLauncherProxyService.getProxy().onAssistantAvailable(assistantAvailable,
                         longPressHomeEnabled);
             } catch (RemoteException e) {
                 Log.w(TAG, "Unable to send assistant availability data to launcher");
@@ -1926,30 +1934,37 @@
             };
 
     @VisibleForTesting
-    int getNavigationIconHints() {
-        return mNavigationIconHints;
+    @NavbarFlags
+    int getNavbarFlags() {
+        return mNavbarFlags;
     }
 
-    private void setNavigationIconHints(int hints) {
-        if (hints == mNavigationIconHints) return;
+    /**
+     * Sets the navigation bar state flags.
+     *
+     * @param flags the navigation bar state flags.
+     */
+    private void setNavbarFlags(@NavbarFlags int flags) {
+        if (flags == mNavbarFlags) {
+            return;
+        }
         if (!isLargeScreen(mContext)) {
             // All IME functions handled by launcher via Sysui flags for large screen
-            final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
-            final boolean oldBackAlt =
-                    (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
-            if (newBackAlt != oldBackAlt) {
-                mView.onBackAltChanged(newBackAlt);
+            final boolean backDismissIme = (flags & StatusBarManager.NAVBAR_BACK_DISMISS_IME) != 0;
+            final boolean oldBackDismissIme =
+                    (mNavbarFlags & StatusBarManager.NAVBAR_BACK_DISMISS_IME) != 0;
+            if (backDismissIme != oldBackDismissIme) {
+                mView.onBackDismissImeChanged(backDismissIme);
             }
-            mImeVisible = (hints & NAVIGATION_HINT_IME_SHOWN) != 0;
+            mImeVisible = (flags & NAVBAR_IME_VISIBLE) != 0;
 
-            mView.setNavigationIconHints(hints);
+            mView.setNavbarFlags(flags);
         }
         if (DEBUG) {
-            android.widget.Toast.makeText(mContext,
-                    "Navigation icon hints = " + hints,
-                    500).show();
+            android.widget.Toast.makeText(mContext, "Navbar flags = " + flags, 500)
+                    .show();
         }
-        mNavigationIconHints = hints;
+        mNavbarFlags = flags;
     }
 
     /**
@@ -2093,7 +2108,7 @@
             if (!canShowSecondaryHandle()) {
                 resetSecondaryHandle();
             }
-            mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI());
+            mView.setShouldShowSwipeUpUi(mLauncherProxyService.shouldShowSwipeUpUI());
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarInflaterView.java
index 96b730c..2c5a9c8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarInflaterView.java
@@ -41,7 +41,7 @@
 import com.android.systemui.navigationbar.views.buttons.KeyButtonView;
 import com.android.systemui.navigationbar.views.buttons.ReverseLinearLayout;
 import com.android.systemui.navigationbar.views.buttons.ReverseLinearLayout.ReverseRelativeLayout;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.res.R;
 import com.android.systemui.shared.system.QuickStepContract;
 
@@ -117,13 +117,13 @@
     private boolean mIsVertical;
     private boolean mAlternativeOrder;
 
-    private OverviewProxyService mOverviewProxyService;
+    private LauncherProxyService mLauncherProxyService;
     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
 
     public NavigationBarInflaterView(Context context, AttributeSet attrs) {
         super(context, attrs);
         createInflaters();
-        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+        mLauncherProxyService = Dependency.get(LauncherProxyService.class);
         mListener = new Listener(this);
         mNavBarMode = Dependency.get(NavigationModeController.class).addListener(mListener);
     }
@@ -159,7 +159,7 @@
     protected String getDefaultLayout() {
         final int defaultResource = QuickStepContract.isGesturalMode(mNavBarMode)
                 ? R.string.config_navBarLayoutHandle
-                : mOverviewProxyService.shouldShowSwipeUpUI()
+                : mLauncherProxyService.shouldShowSwipeUpUI()
                         ? R.string.config_navBarLayoutQuickstep
                         : R.string.config_navBarLayout;
         return getContext().getString(defaultResource);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
index d5ae721..36cb8fa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.navigationbar.views;
 
+import static android.app.StatusBarManager.NAVBAR_BACK_DISMISS_IME;
+import static android.app.StatusBarManager.NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
+import static android.app.StatusBarManager.NAVBAR_IME_VISIBLE;
 import static android.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
@@ -31,7 +34,7 @@
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.annotation.DrawableRes;
-import android.app.StatusBarManager;
+import android.app.StatusBarManager.NavbarFlags;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
@@ -113,7 +116,8 @@
 
     boolean mLongClickableAccessibilityButton;
     int mDisabledFlags = 0;
-    int mNavigationIconHints = 0;
+    @NavbarFlags
+    private int mNavbarFlags;
     private int mNavBarMode;
     private boolean mImeDrawsImeNavBar;
 
@@ -176,7 +180,7 @@
      */
     private final boolean mImeCanRenderGesturalNavButtons = canImeRenderGesturalNavButtons();
     private Gefingerpoken mTouchHandler;
-    private boolean mOverviewProxyEnabled;
+    private boolean mLauncherProxyEnabled;
     private boolean mShowSwipeUpUi;
     private UpdateActiveTouchRegionsCallback mUpdateActiveTouchRegionsCallback;
 
@@ -210,7 +214,11 @@
             }
         }
 
-        public void onBackAltCleared() {
+        /**
+         * Called when the back button is no longer visually adjusted to indicate that it will
+         * dismiss the IME when pressed.
+         */
+        public void onBackDismissImeCleared() {
             ButtonDispatcher backButton = getBackButton();
 
             // When dismissing ime during unlock, force the back button to run the same appearance
@@ -499,10 +507,9 @@
     }
 
     private void orientBackButton(KeyButtonDrawable drawable) {
-        final boolean useAltBack =
-                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+        final boolean isBackDismissIme = (mNavbarFlags & NAVBAR_BACK_DISMISS_IME) != 0;
         final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        float degrees = useAltBack ? (isRtl ? 90 : -90) : 0;
+        float degrees = isBackDismissIme ? (isRtl ? 90 : -90) : 0;
         if (drawable.getRotation() == degrees) {
             return;
         }
@@ -514,7 +521,7 @@
 
         // Animate the back button's rotation to the new degrees and only in portrait move up the
         // back button to line up with the other buttons
-        float targetY = !mShowSwipeUpUi && !mIsVertical && useAltBack
+        float targetY = !mShowSwipeUpUi && !mIsVertical && isBackDismissIme
                 ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset)
                 : 0;
         ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable,
@@ -555,22 +562,25 @@
         super.setLayoutDirection(layoutDirection);
     }
 
-    void setNavigationIconHints(int hints) {
-        if (hints == mNavigationIconHints) return;
-        mNavigationIconHints = hints;
+    void setNavbarFlags(@NavbarFlags int flags) {
+        if (flags == mNavbarFlags) {
+            return;
+        }
+        mNavbarFlags = flags;
         updateNavButtonIcons();
     }
 
     /**
-     * Called when the boolean value of whether to adjust the back button for the IME changed.
+     * Called when the state of the back button being visually adjusted to indicate that it will
+     * dismiss the IME when pressed has changed.
      *
-     * @param useBackAlt whether to adjust the back button for the IME.
+     * @param isBackDismissIme whether the back button is adjusted for IME dismissal.
      *
      * @see android.inputmethodservice.InputMethodService.BackDispositionMode
      */
-    void onBackAltChanged(boolean useBackAlt) {
-        if (!useBackAlt) {
-            mTransitionListener.onBackAltCleared();
+    void onBackDismissImeChanged(boolean isBackDismissIme) {
+        if (!isBackDismissIme) {
+            mTransitionListener.onBackDismissImeCleared();
         }
     }
 
@@ -594,8 +604,7 @@
         // We have to replace or restore the back and home button icons when exiting or entering
         // carmode, respectively. Recents are not available in CarMode in nav bar so change
         // to recent icon is not required.
-        final boolean useAltBack =
-                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+        final boolean isBackDismissIme = (mNavbarFlags & NAVBAR_BACK_DISMISS_IME) != 0;
         KeyButtonDrawable backIcon = mBackIcon;
         orientBackButton(backIcon);
         KeyButtonDrawable homeIcon = mHomeDefaultIcon;
@@ -607,11 +616,12 @@
 
         updateRecentsIcon();
 
-        // Update IME button visibility, a11y and rotate button always overrides the appearance
-        boolean disableImeSwitcher =
-                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0
-                || isImeRenderingNavButtons();
-        mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher);
+        // Update IME switcher button visibility, a11y and rotate button always overrides
+        // the appearance.
+        final boolean isImeSwitcherButtonVisible =
+                (mNavbarFlags & NAVBAR_IME_SWITCHER_BUTTON_VISIBLE) != 0
+                        && !isImeRenderingNavButtons();
+        mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, isImeSwitcherButtonVisible);
 
         mBarTransitions.reapplyDarkIntensity();
 
@@ -625,14 +635,14 @@
         boolean disableHomeHandle = disableRecent
                 && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
 
-        boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures()
+        boolean disableBack = !isBackDismissIme && (mEdgeBackGestureHandler.isHandlingGestures()
                 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0))
                 || isImeRenderingNavButtons();
 
         // When screen pinning, don't hide back and home when connected service or back and
         // recents buttons when disconnected from launcher service in screen pinning mode,
         // as they are used for exiting.
-        if (mOverviewProxyEnabled) {
+        if (mLauncherProxyEnabled) {
             // Force disable recents when not in legacy mode
             disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode);
             if (mScreenPinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) {
@@ -663,9 +673,8 @@
      * Returns whether the IME is currently visible and drawing the nav buttons.
      */
     boolean isImeRenderingNavButtons() {
-        return mImeDrawsImeNavBar
-                && mImeCanRenderGesturalNavButtons
-                && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
+        return mImeDrawsImeNavBar && mImeCanRenderGesturalNavButtons
+                && (mNavbarFlags & NAVBAR_IME_VISIBLE) != 0;
     }
 
     @VisibleForTesting
@@ -755,8 +764,8 @@
         }
     }
 
-    void onOverviewProxyConnectionChange(boolean enabled) {
-        mOverviewProxyEnabled = enabled;
+    void onLauncherProxyConnectionChange(boolean enabled) {
+        mLauncherProxyEnabled = enabled;
     }
 
     void setShouldShowSwipeUpUi(boolean showSwipeUpUi) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/KeyButtonView.java
index 111a2d4..32a03e5 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/KeyButtonView.java
@@ -61,7 +61,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Dependency;
 import com.android.systemui.assist.AssistManager;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.res.R;
 import com.android.systemui.shared.navigationbar.KeyButtonRipple;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -82,7 +82,7 @@
     @VisibleForTesting boolean mLongClicked;
     private OnClickListener mOnClickListener;
     private final KeyButtonRipple mRipple;
-    private final OverviewProxyService mOverviewProxyService;
+    private final LauncherProxyService mLauncherProxyService;
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
     private final InputManagerGlobal mInputManagerGlobal;
     private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
@@ -181,7 +181,7 @@
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
 
         mRipple = new KeyButtonRipple(context, this, R.dimen.key_button_ripple_max_width);
-        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+        mLauncherProxyService = Dependency.get(LauncherProxyService.class);
         mInputManagerGlobal = manager;
         setBackground(mRipple);
         setWillNotDraw(false);
@@ -282,7 +282,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        final boolean showSwipeUI = mOverviewProxyService.shouldShowSwipeUpUI();
+        final boolean showSwipeUI = mLauncherProxyService.shouldShowSwipeUpUI();
         final int action = ev.getAction();
         int x, y;
         if (action == MotionEvent.ACTION_DOWN) {
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
index 195b0ce..1b92510 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
@@ -21,7 +21,8 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
 import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
@@ -38,7 +39,10 @@
                 Swipe.Up to HideOverlay(Overlays.NotificationsShade),
                 Back to HideOverlay(Overlays.NotificationsShade),
                 Swipe.Down(fromSource = SceneContainerEdge.TopRight) to
-                    ReplaceByOverlay(Overlays.QuickSettingsShade),
+                    ShowOverlay(
+                        Overlays.QuickSettingsShade,
+                        hideCurrentOverlays = HideCurrentOverlays.Some(Overlays.NotificationsShade),
+                    ),
             )
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
deleted file mode 100644
index 398ace4..0000000
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.notifications.ui.viewmodel
-
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
-import com.android.systemui.scene.shared.model.Overlays
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
-import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-
-/**
- * Models the UI state for the user actions that the user can perform to navigate to other scenes.
- */
-class NotificationsShadeUserActionsViewModel @AssistedInject constructor() :
-    UserActionsViewModel() {
-
-    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
-        setActions(
-            mapOf(
-                Back to SceneFamilies.Home,
-                Swipe.Up to SceneFamilies.Home,
-                Swipe.Down(fromSource = SceneContainerEdge.TopRight) to
-                    ReplaceByOverlay(Overlays.QuickSettingsShade),
-            )
-        )
-    }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): NotificationsShadeUserActionsViewModel
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
index f15a7b3..f8d442d 100644
--- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -22,6 +22,8 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.classifier.FalsingCollectorActual
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.data.repository.PowerRepository
 import com.android.systemui.power.shared.model.DozeScreenStateModel
@@ -35,6 +37,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 
@@ -228,6 +231,15 @@
         repository.updateWakefulness(powerButtonLaunchGestureTriggered = true)
     }
 
+    suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+        detailedWakefulness
+            .logDiffsForTable(
+                tableLogBuffer = tableLogBuffer,
+                initialValue = detailedWakefulness.value,
+            )
+            .collect()
+    }
+
     companion object {
         private const val FSI_WAKE_WHY = "full_screen_intent"
 
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
index 0f49c94..297c6af 100644
--- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
@@ -1,6 +1,8 @@
 package com.android.systemui.power.shared.model
 
 import com.android.systemui.keyguard.KeyguardService
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
 
 /**
  * Models whether the device is awake or asleep, along with information about why we're in that
@@ -35,7 +37,7 @@
      * to a subsequent power gesture.
      */
     val powerButtonLaunchGestureTriggered: Boolean = false,
-) {
+) : Diffable<WakefulnessModel> {
     fun isAwake() =
         internalWakefulnessState == WakefulnessState.AWAKE ||
             internalWakefulnessState == WakefulnessState.STARTING_TO_WAKE
@@ -58,4 +60,8 @@
         return isAwake() &&
             (lastWakeReason == WakeSleepReason.TAP || lastWakeReason == WakeSleepReason.GESTURE)
     }
+
+    override fun logDiffs(prevVal: WakefulnessModel, row: TableRowLogger) {
+        row.logChange(columnName = "wakefulness", value = toString())
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index afb852a..c8f7be6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs;
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import static com.android.systemui.Flags.quickSettingsVisualHapticsLongpress;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -364,12 +363,7 @@
     }
 
     private void addTile(final QSTile tile, boolean collapsedView) {
-        QSLongPressEffect longPressEffect;
-        if (quickSettingsVisualHapticsLongpress()) {
-            longPressEffect = mLongPressEffectProvider.get();
-        } else {
-            longPressEffect = null;
-        }
+        QSLongPressEffect longPressEffect = mLongPressEffectProvider.get();
         final QSTileViewImpl tileView = new QSTileViewImpl(
                 getContext(), collapsedView, longPressEffect);
         final TileRecord r = new TileRecord(tile, tileView);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 07de466..85b677b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -32,11 +32,7 @@
 import androidx.activity.OnBackPressedDispatcherOwner
 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
 import androidx.annotation.VisibleForTesting
-import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.core.tween
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.layout.Arrangement.spacedBy
 import androidx.compose.foundation.layout.Box
@@ -120,6 +116,7 @@
 import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams
 import com.android.systemui.qs.composefragment.ui.notificationScrimClip
 import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings
+import com.android.systemui.qs.composefragment.ui.toEditMode
 import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel
 import com.android.systemui.qs.flags.QSComposeFragment
 import com.android.systemui.qs.footer.ui.compose.FooterActions
@@ -144,6 +141,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 
@@ -273,36 +271,7 @@
                                 // by the composables.
                                 .gesturesDisabled(viewModel.showingMirror)
                     ) {
-                        val isEditing by
-                            viewModel.containerViewModel.editModeViewModel.isEditing
-                                .collectAsStateWithLifecycle()
-                        val animationSpecEditMode = tween<Float>(EDIT_MODE_TIME_MILLIS)
-                        AnimatedContent(
-                            targetState = isEditing,
-                            transitionSpec = {
-                                fadeIn(animationSpecEditMode) togetherWith
-                                    fadeOut(animationSpecEditMode)
-                            },
-                            label = "EditModeAnimatedContent",
-                        ) { editing ->
-                            if (editing) {
-                                val qqsPadding = viewModel.qqsHeaderHeight
-                                EditMode(
-                                    viewModel = viewModel.containerViewModel.editModeViewModel,
-                                    modifier =
-                                        Modifier.fillMaxWidth()
-                                            .padding(top = { qqsPadding })
-                                            .padding(
-                                                horizontal = {
-                                                    QuickSettingsShade.Dimensions.Padding
-                                                        .roundToPx()
-                                                }
-                                            ),
-                                )
-                            } else {
-                                CollapsableQuickSettingsSTL()
-                            }
-                        }
+                        CollapsableQuickSettingsSTL()
                     }
                 }
             }
@@ -324,12 +293,17 @@
                         from(QuickQuickSettings, QuickSettings) {
                             quickQuickSettingsToQuickSettings(viewModel::animateTilesExpansion::get)
                         }
+                        to(SceneKeys.EditMode) {
+                            spec = tween(durationMillis = EDIT_MODE_TIME_MILLIS)
+                            toEditMode()
+                        }
                     },
             )
 
         LaunchedEffect(Unit) {
             synchronizeQsState(
                 sceneState,
+                viewModel.containerViewModel.editModeViewModel.isEditing,
                 snapshotFlow { viewModel.expansionState }.map { it.progress },
             )
         }
@@ -337,12 +311,20 @@
         SceneTransitionLayout(state = sceneState, modifier = Modifier.fillMaxSize()) {
             scene(QuickSettings) {
                 LaunchedEffect(Unit) { viewModel.onQSOpen() }
-                QuickSettingsElement()
+                QuickSettingsElement(Modifier.element(QuickSettings.rootElementKey))
             }
 
             scene(QuickQuickSettings) {
                 LaunchedEffect(Unit) { viewModel.onQQSOpen() }
-                QuickQuickSettingsElement()
+                // Cannot pass the element modifier in because the top element has a `testTag`
+                // and this would overwrite it.
+                Box(Modifier.element(QuickQuickSettings.rootElementKey)) {
+                    QuickQuickSettingsElement()
+                }
+            }
+
+            scene(SceneKeys.EditMode) {
+                EditModeElement(Modifier.element(SceneKeys.EditMode.rootElementKey))
             }
         }
     }
@@ -582,7 +564,7 @@
     }
 
     @Composable
-    private fun ContentScope.QuickQuickSettingsElement() {
+    private fun ContentScope.QuickQuickSettingsElement(modifier: Modifier = Modifier) {
         val qqsPadding = viewModel.qqsHeaderHeight
         val bottomPadding = viewModel.qqsBottomPadding
         DisposableEffect(Unit) {
@@ -595,7 +577,7 @@
                 .squishiness
                 .collectAsStateWithLifecycle()
 
-        Column(modifier = Modifier.sysuiResTag(ResIdTags.quickQsPanel)) {
+        Column(modifier = modifier.sysuiResTag(ResIdTags.quickQsPanel)) {
             Box(
                 modifier =
                     Modifier.fillMaxWidth()
@@ -666,12 +648,12 @@
     }
 
     @Composable
-    private fun ContentScope.QuickSettingsElement() {
+    private fun ContentScope.QuickSettingsElement(modifier: Modifier = Modifier) {
         val qqsPadding = viewModel.qqsHeaderHeight
         val qsExtraPadding = dimensionResource(R.dimen.qs_panel_padding_top)
         Column(
             modifier =
-                Modifier.collapseExpandSemanticAction(
+                modifier.collapseExpandSemanticAction(
                     stringResource(id = R.string.accessibility_quick_settings_collapse)
                 )
         ) {
@@ -776,6 +758,18 @@
         }
     }
 
+    @Composable
+    private fun EditModeElement(modifier: Modifier = Modifier) {
+        // No need for top padding, the Scaffold inside takes care of the correct insets
+        EditMode(
+            viewModel = viewModel.containerViewModel.editModeViewModel,
+            modifier =
+                modifier
+                    .fillMaxWidth()
+                    .padding(horizontal = { QuickSettingsShade.Dimensions.Padding.roundToPx() }),
+        )
+    }
+
     private fun Modifier.collapseExpandSemanticAction(label: String): Modifier {
         return viewModel.collapseExpandAccessibilityAction?.let {
             semantics {
@@ -863,6 +857,7 @@
 object SceneKeys {
     val QuickQuickSettings = SceneKey("QuickQuickSettingsScene")
     val QuickSettings = SceneKey("QuickSettingsScene")
+    val EditMode = SceneKey("EditModeScene")
 
     fun QSFragmentComposeViewModel.QSExpansionState.toIdleSceneKey(): SceneKey {
         return when {
@@ -880,7 +875,11 @@
         }
 }
 
-suspend fun synchronizeQsState(state: MutableSceneTransitionLayoutState, expansion: Flow<Float>) {
+private suspend fun synchronizeQsState(
+    state: MutableSceneTransitionLayoutState,
+    editMode: Flow<Boolean>,
+    expansion: Flow<Float>,
+) {
     coroutineScope {
         val animationScope = this
 
@@ -891,23 +890,30 @@
             currentTransition = null
         }
 
-        expansion.collectLatest { progress ->
-            when (progress) {
-                0f -> snapTo(QuickQuickSettings)
-                1f -> snapTo(QuickSettings)
-                else -> {
-                    val transition = currentTransition
-                    if (transition != null) {
-                        transition.progress = progress
-                        return@collectLatest
-                    }
+        editMode.combine(expansion, ::Pair).collectLatest { (editMode, progress) ->
+            if (editMode && state.currentScene != SceneKeys.EditMode) {
+                state.setTargetScene(SceneKeys.EditMode, animationScope)?.second?.join()
+            } else if (!editMode && state.currentScene == SceneKeys.EditMode) {
+                state.setTargetScene(SceneKeys.QuickSettings, animationScope)?.second?.join()
+            }
+            if (!editMode) {
+                when (progress) {
+                    0f -> snapTo(QuickQuickSettings)
+                    1f -> snapTo(QuickSettings)
+                    else -> {
+                        val transition = currentTransition
+                        if (transition != null) {
+                            transition.progress = progress
+                            return@collectLatest
+                        }
 
-                    val newTransition =
-                        ExpansionTransition(progress).also { currentTransition = it }
-                    state.startTransitionImmediately(
-                        animationScope = animationScope,
-                        transition = newTransition,
-                    )
+                        val newTransition =
+                            ExpansionTransition(progress).also { currentTransition = it }
+                        state.startTransitionImmediately(
+                            animationScope = animationScope,
+                            transition = newTransition,
+                        )
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/ToEditMode.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/ToEditMode.kt
new file mode 100644
index 0000000..0c6f3ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/ToEditMode.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2025 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.qs.composefragment.ui
+
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.qs.composefragment.SceneKeys
+
+fun TransitionBuilder.toEditMode() {
+    fractionRange(start = 0.5f) { fade(SceneKeys.EditMode.rootElementKey) }
+    fractionRange(end = 0.5f) {
+        fade(SceneKeys.QuickQuickSettings.rootElementKey)
+        fade(SceneKeys.QuickSettings.rootElementKey)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index 07ceb64..219fc2f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -81,7 +81,6 @@
 import dagger.assisted.AssistedInject
 import java.io.PrintWriter
 import javax.inject.Named
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.coroutineScope
@@ -91,7 +90,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 
-@OptIn(ExperimentalCoroutinesApi::class)
 class QSFragmentComposeViewModel
 @AssistedInject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index cc87206..9546e35 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -38,14 +38,14 @@
 import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.util.icuMessageFormat
 import javax.inject.Inject
 import javax.inject.Named
 import javax.inject.Provider
 import kotlin.math.max
 import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,6 +53,7 @@
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.isActive
 
@@ -73,7 +74,7 @@
     val settings: FooterActionsButtonViewModel,
 
     /** The model for the power button. */
-    val power: FooterActionsButtonViewModel?,
+    val power: Flow<FooterActionsButtonViewModel?>,
 
     /**
      * Observe the device monitoring dialog requests and show the dialog accordingly. This function
@@ -116,6 +117,7 @@
         @ShadeDisplayAware private val context: Context,
         private val falsingManager: FalsingManager,
         private val footerActionsInteractor: FooterActionsInteractor,
+        private val shadeModeInteractor: ShadeModeInteractor,
         private val globalActionsDialogLiteProvider: Provider<GlobalActionsDialogLite>,
         private val activityStarter: ActivityStarter,
         @Named(PM_LITE_ENABLED) private val showPowerButton: Boolean,
@@ -138,9 +140,10 @@
                 )
             }
 
-            return FooterActionsViewModel(
+            return createFooterActionsViewModel(
                 context,
                 footerActionsInteractor,
+                shadeModeInteractor.shadeMode,
                 falsingManager,
                 globalActionsDialogLite,
                 activityStarter,
@@ -148,7 +151,6 @@
             )
         }
 
-        @OptIn(ExperimentalCoroutinesApi::class)
         fun create(lifecycleCoroutineScope: LifecycleCoroutineScope): FooterActionsViewModel {
             val globalActionsDialogLite = globalActionsDialogLiteProvider.get()
             if (lifecycleCoroutineScope.isActive) {
@@ -163,9 +165,10 @@
                 globalActionsDialogLite.destroy()
             }
 
-            return FooterActionsViewModel(
+            return createFooterActionsViewModel(
                 context,
                 footerActionsInteractor,
+                shadeModeInteractor.shadeMode,
                 falsingManager,
                 globalActionsDialogLite,
                 activityStarter,
@@ -175,9 +178,10 @@
     }
 }
 
-fun FooterActionsViewModel(
+fun createFooterActionsViewModel(
     @ShadeDisplayAware appContext: Context,
     footerActionsInteractor: FooterActionsInteractor,
+    shadeMode: Flow<ShadeMode>,
     falsingManager: FalsingManager,
     globalActionsDialogLite: GlobalActionsDialogLite,
     activityStarter: ActivityStarter,
@@ -272,11 +276,12 @@
         userSwitcherViewModel(qsThemedContext, footerActionsInteractor, ::onUserSwitcherClicked)
 
     val settings = settingsButtonViewModel(qsThemedContext, ::onSettingsButtonClicked)
+
     val power =
         if (showPowerButton) {
-            powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked)
+            powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked, shadeMode)
         } else {
-            null
+            flowOf(null)
         }
 
     return FooterActionsViewModel(
@@ -403,19 +408,23 @@
 fun powerButtonViewModel(
     qsThemedContext: Context,
     onPowerButtonClicked: (Expandable) -> Unit,
-): FooterActionsButtonViewModel {
-    return FooterActionsButtonViewModel(
-        id = R.id.pm_lite,
-        Icon.Resource(
-            android.R.drawable.ic_lock_power_off,
-            ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu),
-        ),
-        iconTint =
-            Utils.getColorAttrDefaultColor(
-                qsThemedContext,
-                if (DualShade.isEnabled) R.attr.onShadeInactiveVariant else R.attr.onShadeActive,
+    shadeMode: Flow<ShadeMode>,
+): Flow<FooterActionsButtonViewModel?> {
+    return shadeMode.map { mode ->
+        val isDualShade = mode is ShadeMode.Dual
+        FooterActionsButtonViewModel(
+            id = R.id.pm_lite,
+            Icon.Resource(
+                android.R.drawable.ic_lock_power_off,
+                ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu),
             ),
-        backgroundColor = if (DualShade.isEnabled) R.attr.shadeInactive else R.attr.shadeActive,
-        onPowerButtonClicked,
-    )
+            iconTint =
+                Utils.getColorAttrDefaultColor(
+                    qsThemedContext,
+                    if (isDualShade) R.attr.onShadeInactiveVariant else R.attr.onShadeActive,
+                ),
+            backgroundColor = if (isDualShade) R.attr.shadeInactive else R.attr.shadeActive,
+            onPowerButtonClicked,
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt
index eeec9b3..f83c2fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt
@@ -25,14 +25,12 @@
 import com.android.systemui.util.kotlin.emitOnStart
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class LargeTileSpanRepository
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt
index 693681d..b45d362 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt
@@ -23,12 +23,10 @@
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.util.kotlin.emitOnStart
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.mapLatest
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class QSColumnsRepository
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt
index b0c6073..19e4fd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt
@@ -34,7 +34,6 @@
 import com.android.systemui.util.kotlin.emitOnStart
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
@@ -43,7 +42,6 @@
 import kotlinx.coroutines.flow.onEach
 
 /** Repository for QS user preferences. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class QSPreferencesRepository
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
index e493cbe..b949754 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
@@ -22,12 +22,10 @@
 import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 
 @SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
 class GridLayoutTypeInteractor
 @Inject
 constructor(private val repo: GridLayoutTypeRepository, shadeModeInteractor: ShadeModeInteractor) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt
index 11ea60e..2b2a63b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -37,7 +36,6 @@
     repo: QSColumnsRepository,
     shadeInteractor: ShadeInteractor,
 ) {
-    @OptIn(ExperimentalCoroutinesApi::class)
     val columns: StateFlow<Int> =
         shadeInteractor.shadeMode
             .flatMapLatest {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
index c9d767e..302242c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.qs.panels.ui.compose
 
-import android.processor.immutability.Immutable
+import androidx.compose.runtime.Stable
 import com.android.compose.animation.Bounceable
 import com.android.systemui.qs.panels.shared.model.SizedTile
 import com.android.systemui.qs.panels.ui.model.GridCell
@@ -24,7 +24,7 @@
 import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
 
-@Immutable
+@Stable
 data class BounceableInfo(
     val bounceable: BounceableTileViewModel,
     val previousTile: Bounceable?,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index 5cb30b9..b084f79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -19,8 +19,8 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.key
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
@@ -49,6 +49,8 @@
     val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
     val scope = rememberCoroutineScope()
 
+    val spans by remember(sizedTiles) { derivedStateOf { sizedTiles.fastMap { it.width } } }
+
     DisposableEffect(tiles) {
         val token = Any()
         tiles.forEach { it.startListening(token) }
@@ -62,26 +64,24 @@
             columns = columns,
             columnSpacing = dimensionResource(R.dimen.qs_tile_margin_horizontal),
             rowSpacing = dimensionResource(R.dimen.qs_tile_margin_vertical),
-            spans = sizedTiles.fastMap { it.width },
+            spans = spans,
             modifier = Modifier.sysuiResTag("qqs_tile_layout"),
+            keys = { sizedTiles[it].tile.spec },
         ) { spanIndex ->
             val it = sizedTiles[spanIndex]
             val column = cellIndex % columns
             cellIndex += it.width
-            key(it.tile.spec) {
-                Tile(
-                    tile = it.tile,
-                    iconOnly = it.isIcon,
-                    modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
-                    squishiness = { squishiness },
-                    coroutineScope = scope,
-                    bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
-                    tileHapticsViewModelFactoryProvider =
-                        viewModel.tileHapticsViewModelFactoryProvider,
-                    // There should be no QuickQuickSettings when the details view is enabled.
-                    detailsViewModel = null,
-                )
-            }
+            Tile(
+                tile = it.tile,
+                iconOnly = it.isIcon,
+                modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
+                squishiness = { squishiness },
+                coroutineScope = scope,
+                bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
+                tileHapticsViewModelFactoryProvider = viewModel.tileHapticsViewModelFactoryProvider,
+                // There should be no QuickQuickSettings when the details view is enabled.
+                detailsViewModel = null,
+            )
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index d2ee126..1f4f9f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -153,7 +153,6 @@
 import kotlin.math.abs
 import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 
 object TileType
@@ -293,7 +292,6 @@
     }
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @Composable
 private fun AutoScrollGrid(
     listState: EditTileListState,
@@ -498,11 +496,9 @@
     return ((tileHeight + tilePadding) * rows) + gridPadding * 2
 }
 
-private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any {
+private fun GridCell.key(index: Int): Any {
     return when (this) {
-        is TileGridCell -> {
-            if (dragAndDropState.isMoving(tile.tileSpec)) index else key
-        }
+        is TileGridCell -> key
         is SpacerGridCell -> index
     }
 }
@@ -510,10 +506,13 @@
 /**
  * Adds a list of [GridCell] to the lazy grid
  *
- * @param cells the pairs of [GridCell] to [AnimatableTileViewModel]
+ * @param cells the pairs of [GridCell] to [BounceableTileViewModel]
+ * @param columns the number of columns of this tile grid
  * @param dragAndDropState the [DragAndDropState] for this grid
  * @param selectionState the [MutableSelectionState] for this grid
- * @param onToggleSize the callback when a tile's size is toggled
+ * @param coroutineScope the [CoroutineScope] to be used for the tiles
+ * @param largeTilesSpan the width used for large tiles
+ * @param onResize the callback when a tile has a new [ResizeOperation]
  */
 fun LazyGridScope.EditTiles(
     cells: List<Pair<GridCell, BounceableTileViewModel>>,
@@ -526,7 +525,7 @@
 ) {
     items(
         count = cells.size,
-        key = { cells[it].first.key(it, dragAndDropState) },
+        key = { cells[it].first.key(it) },
         span = { cells[it].first.span },
         contentType = { TileType },
     ) { index ->
@@ -536,13 +535,12 @@
                     // If the tile is being moved, replace it with a visible spacer
                     SpacerGridCell(
                         Modifier.background(
-                                color =
-                                    MaterialTheme.colorScheme.secondary.copy(
-                                        alpha = EditModeTileDefaults.PLACEHOLDER_ALPHA
-                                    ),
-                                shape = RoundedCornerShape(InactiveCornerRadius),
-                            )
-                            .animateItem()
+                            color =
+                                MaterialTheme.colorScheme.secondary.copy(
+                                    alpha = EditModeTileDefaults.PLACEHOLDER_ALPHA
+                                ),
+                            shape = RoundedCornerShape(InactiveCornerRadius),
+                        )
                     )
                 } else {
                     TileGridCell(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 4432d33..cc4c3af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -18,8 +18,8 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.key
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
@@ -86,27 +86,28 @@
         val scope = rememberCoroutineScope()
         var cellIndex = 0
 
+        val spans by remember(sizedTiles) { derivedStateOf { sizedTiles.fastMap { it.width } } }
+
         VerticalSpannedGrid(
             columns = columns,
             columnSpacing = dimensionResource(R.dimen.qs_tile_margin_horizontal),
             rowSpacing = dimensionResource(R.dimen.qs_tile_margin_vertical),
-            spans = sizedTiles.fastMap { it.width },
+            spans = spans,
+            keys = { sizedTiles[it].tile.spec },
         ) { spanIndex ->
             val it = sizedTiles[spanIndex]
             val column = cellIndex % columns
             cellIndex += it.width
-            key(it.tile.spec) {
-                Tile(
-                    tile = it.tile,
-                    iconOnly = iconTilesViewModel.isIconTile(it.tile.spec),
-                    modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
-                    squishiness = { squishiness },
-                    tileHapticsViewModelFactoryProvider = tileHapticsViewModelFactoryProvider,
-                    coroutineScope = scope,
-                    bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
-                    detailsViewModel = detailsViewModel,
-                )
-            }
+            Tile(
+                tile = it.tile,
+                iconOnly = iconTilesViewModel.isIconTile(it.tile.spec),
+                modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
+                squishiness = { squishiness },
+                tileHapticsViewModelFactoryProvider = tileHapticsViewModelFactoryProvider,
+                coroutineScope = scope,
+                bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
+                detailsViewModel = detailsViewModel,
+            )
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt
index 37b1642..59c554c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt
@@ -43,6 +43,7 @@
         )
 
         Spacer(modifier = Modifier.weight(1f))
-        IconButton(viewModel.powerButtonViewModel, Modifier.sysuiResTag("pm_lite"))
+
+        viewModel.powerButtonViewModel?.let { IconButton(it, Modifier.sysuiResTag("pm_lite")) }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index f7ed1ad..2082423 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -42,7 +42,6 @@
 import javax.inject.Named
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -55,7 +54,6 @@
 import kotlinx.coroutines.launch
 
 @SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
 class EditModeViewModel
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModel.kt
index 127ecb2..29832b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModel.kt
@@ -25,7 +25,6 @@
 import javax.inject.Inject
 import javax.inject.Named
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -33,7 +32,6 @@
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class TileGridViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt
index 0fde855..1a6653c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.qs.footer.ui.viewmodel.userSwitcherViewModel
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import javax.inject.Provider
@@ -48,13 +49,24 @@
     private val footerActionsInteractor: FooterActionsInteractor,
     private val globalActionsDialogLiteProvider: Provider<GlobalActionsDialogLite>,
     private val falsingInteractor: FalsingInteractor,
+    shadeModeInteractor: ShadeModeInteractor,
     @ShadeDisplayAware appContext: Context,
 ) : ExclusiveActivatable() {
     private val qsThemedContext =
         ContextThemeWrapper(appContext, R.style.Theme_SystemUI_QuickSettings)
     private val hydrator = Hydrator("ToolbarViewModel.hydrator")
 
-    val powerButtonViewModel = powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked)
+    val powerButtonViewModel: FooterActionsButtonViewModel? by
+        hydrator.hydratedStateOf(
+            traceName = "powerButtonViewModel",
+            initialValue = null,
+            source =
+                powerButtonViewModel(
+                    qsThemedContext,
+                    ::onPowerButtonClicked,
+                    shadeModeInteractor.shadeMode,
+                ),
+        )
 
     val settingsButtonViewModel =
         settingsButtonViewModel(qsThemedContext, ::onSettingsButtonClicked)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
index a2bb9e9..1cd5d10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
@@ -20,7 +20,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -75,7 +74,6 @@
             }
             .emitOnStart()
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     override val restoreData =
         run {
                 val mutex = Mutex()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index c6751b7..4e99378 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.retail.data.repository.RetailModeRepository
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
@@ -87,7 +86,6 @@
  * If the device is in retail mode, the tiles are fixed to the value of
  * [R.string.quick_settings_tiles_retail_mode].
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class TileSpecSettingsRepository
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 2bb6bba..609541b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -52,7 +52,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -133,7 +132,6 @@
  * * Platform tiles will be kept between users, with a call to [QSTile.userSwitch]
  * * [CustomTile]s will only be destroyed if the user changes.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CurrentTilesInteractorImpl
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
index 180e0fe..1a28478 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
@@ -11,7 +11,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flatMapConcat
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.take
@@ -40,7 +39,6 @@
     @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) {
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     fun start() {
         applicationScope.launch(context = backgroundDispatcher) {
             qsSettingsRestoredRepository.restoreData
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
index 16c2722..8a627c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName
 import android.text.TextUtils
+import androidx.compose.runtime.Stable
 import com.android.systemui.qs.external.CustomTile
 
 /**
@@ -34,6 +35,7 @@
     data object Invalid : TileSpec("")
 
     /** Container for the spec of a tile provided by SystemUI. */
+    @Stable
     data class PlatformTileSpec internal constructor(override val spec: String) : TileSpec(spec) {
         override fun toString(): String {
             return "P($spec)"
@@ -45,6 +47,7 @@
      *
      * [componentName] indicates the associated `TileService`.
      */
+    @Stable
     data class CustomTileSpec
     internal constructor(override val spec: String, val componentName: ComponentName) :
         TileSpec(spec) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 6d3e5d0..b1f99cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -61,6 +61,7 @@
     private val internetDialogManager: InternetDialogManager,
     private val wifiStateWorker: WifiStateWorker,
     private val accessPointController: AccessPointController,
+    private val internetDetailsViewModelFactory: InternetDetailsViewModel.Factory,
 ) :
     QSTileImpl<QSTile.BooleanState>(
         host,
@@ -107,7 +108,7 @@
     }
 
     override fun getDetailsViewModel(): TileDetailsViewModel {
-        return InternetDetailsViewModel { longClick(null) }
+        return internetDetailsViewModelFactory.create { longClick(null) }
     }
 
     override fun handleSecondaryClick(expandable: Expandable?) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index 0051bf5..ad5dd27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -35,12 +35,14 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.TileDetailsViewModel
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.asQSTileIcon
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.dialog.ModesDetailsViewModel
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
@@ -48,6 +50,7 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.runBlocking
 
@@ -67,6 +70,7 @@
     private val dataInteractor: ModesTileDataInteractor,
     private val tileMapper: ModesTileMapper,
     private val userActionInteractor: ModesTileUserActionInteractor,
+    private val modesDialogViewModel: ModesDialogViewModel,
 ) :
     QSTileImpl<QSTile.State>(
         host,
@@ -114,6 +118,13 @@
         userActionInteractor.handleToggleClick(model)
     }
 
+    override fun getDetailsViewModel(): TileDetailsViewModel {
+        return ModesDetailsViewModel(
+            onSettingsClick = { userActionInteractor.handleLongClick(null) },
+            viewModel = modesDialogViewModel,
+        )
+    }
+
     override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index 224fa10..30bf5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -40,7 +40,6 @@
 import java.io.PrintWriter
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -70,7 +69,6 @@
  * Don't use this constructor directly. Instead, inject [QSTileViewModelFactory] to create a new
  * instance of this class.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 class QSTileViewModelImpl<DATA_TYPE>(
     override val config: QSTileConfig,
     private val userActionInteractor: () -> QSTileUserActionInteractor<DATA_TYPE>,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
index c64532a..733159e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
@@ -57,10 +57,8 @@
 import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils
 import com.android.systemui.Prefs
 import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan
-import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.flags.QsDetailedView
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -75,11 +73,7 @@
 
 /**
  * View content for the Internet tile details that handles all UI interactions and state management.
- *
- * @param internetDialog non-null if the details should be shown as part of a dialog and null
- *   otherwise.
  */
-// TODO: b/377388104 Make this content for details view only.
 class InternetDetailsContentManager
 @AssistedInject
 constructor(
@@ -88,9 +82,7 @@
     @Assisted(CAN_CONFIG_WIFI) private val canConfigWifi: Boolean,
     @Assisted private val coroutineScope: CoroutineScope,
     @Assisted private var context: Context,
-    @Assisted private var internetDialog: SystemUIDialog?,
     private val uiEventLogger: UiEventLogger,
-    private val dialogTransitionAnimator: DialogTransitionAnimator,
     @Main private val handler: Handler,
     @Background private val backgroundExecutor: Executor,
     private val keyguard: KeyguardStateController,
@@ -104,8 +96,6 @@
 
     // UI Components
     private lateinit var contentView: View
-    private lateinit var internetDialogTitleView: TextView
-    private lateinit var internetDialogSubTitleView: TextView
     private lateinit var divider: View
     private lateinit var progressBar: ProgressBar
     private lateinit var ethernetLayout: LinearLayout
@@ -132,7 +122,6 @@
     private lateinit var shareWifiButton: Button
     private lateinit var airplaneModeButton: Button
     private var alertDialog: AlertDialog? = null
-    private lateinit var doneButton: Button
 
     private val canChangeWifiState =
         WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context)
@@ -153,7 +142,6 @@
             @Assisted(CAN_CONFIG_WIFI) canConfigWifi: Boolean,
             coroutineScope: CoroutineScope,
             context: Context,
-            internetDialog: SystemUIDialog?,
         ): InternetDetailsContentManager
     }
 
@@ -209,8 +197,6 @@
         }
 
         // Network layouts
-        internetDialogTitleView = contentView.requireViewById(R.id.internet_dialog_title)
-        internetDialogSubTitleView = contentView.requireViewById(R.id.internet_dialog_subtitle)
         divider = contentView.requireViewById(R.id.divider)
         progressBar = contentView.requireViewById(R.id.wifi_searching_progress)
 
@@ -219,15 +205,6 @@
         setMobileLayout()
         ethernetLayout = contentView.requireViewById(R.id.ethernet_layout)
 
-        // Done button is only visible for the dialog view
-        doneButton = contentView.requireViewById(R.id.done_button)
-        if (internetDialog == null) {
-            doneButton.visibility = View.GONE
-        } else {
-            // Set done button if qs details view is not enabled.
-            doneButton.setOnClickListener { internetDialog!!.dismiss() }
-        }
-
         // Share WiFi
         shareWifiButton = contentView.requireViewById(R.id.share_wifi_button)
         shareWifiButton.setOnClickListener { view ->
@@ -251,6 +228,17 @@
         // Background drawables
         backgroundOn = context.getDrawable(R.drawable.settingslib_switch_bar_bg_on)
         backgroundOff = context.getDrawable(R.drawable.internet_dialog_selected_effect)
+
+        // Done button is only visible for the dialog view
+        contentView.findViewById<Button>(R.id.done_button).apply { visibility = View.GONE }
+
+        // Title and subtitle will be added in the `TileDetails`
+        contentView.findViewById<TextView>(R.id.internet_dialog_title).apply {
+            visibility = View.GONE
+        }
+        contentView.findViewById<TextView>(R.id.internet_dialog_subtitle).apply {
+            visibility = View.GONE
+        }
     }
 
     private fun setWifiLayout() {
@@ -336,21 +324,19 @@
         }
     }
 
-    private fun getDialogTitleText(): CharSequence {
-        return internetDetailsContentController.getDialogTitleText()
+    fun getTitleText(): String {
+        return internetDetailsContentController.getDialogTitleText().toString()
+    }
+
+    fun getSubtitleText(): String {
+        return internetDetailsContentController.getSubtitleText(isProgressBarVisible).toString()
     }
 
     private fun updateDetailsUI(internetContent: InternetContent) {
         if (DEBUG) {
             Log.d(TAG, "updateDetailsUI ")
         }
-        if (QsDetailedView.isEnabled) {
-            internetDialogTitleView.visibility = View.GONE
-            internetDialogSubTitleView.visibility = View.GONE
-        } else {
-            internetDialogTitleView.text = internetContent.internetDialogTitleString
-            internetDialogSubTitleView.text = internetContent.internetDialogSubTitle
-        }
+
         airplaneModeButton.visibility =
             if (internetContent.isAirplaneModeEnabled) View.VISIBLE else View.GONE
 
@@ -361,17 +347,11 @@
 
     private fun getStartingInternetContent(): InternetContent {
         return InternetContent(
-            internetDialogTitleString = getDialogTitleText(),
-            internetDialogSubTitle = getSubtitleText(),
             isWifiEnabled = internetDetailsContentController.isWifiEnabled,
             isDeviceLocked = internetDetailsContentController.isDeviceLocked,
         )
     }
 
-    private fun getSubtitleText(): String {
-        return internetDetailsContentController.getSubtitleText(isProgressBarVisible).toString()
-    }
-
     @VisibleForTesting
     internal fun hideWifiViews() {
         setProgressBarVisible(false)
@@ -393,7 +373,6 @@
         progressBar.visibility = if (visible) View.VISIBLE else View.GONE
         progressBar.isIndeterminate = visible
         divider.visibility = if (visible) View.GONE else View.VISIBLE
-        internetDialogSubTitleView.text = getSubtitleText()
     }
 
     private fun showTurnOffAutoDataSwitchDialog(subId: Int) {
@@ -418,12 +397,7 @@
         SystemUIDialog.setShowForAllUsers(alertDialog, true)
         SystemUIDialog.registerDismissListener(alertDialog)
         SystemUIDialog.setWindowOnTop(alertDialog, keyguard.isShowing())
-        if (QsDetailedView.isEnabled) {
-            alertDialog!!.show()
-        } else {
-            dialogTransitionAnimator.showFromDialog(alertDialog!!, internetDialog!!, null, false)
-            Log.e(TAG, "Internet dialog is shown with the refactor code")
-        }
+        alertDialog!!.show()
     }
 
     private fun shouldShowMobileDialog(): Boolean {
@@ -466,11 +440,8 @@
         SystemUIDialog.setShowForAllUsers(alertDialog, true)
         SystemUIDialog.registerDismissListener(alertDialog)
         SystemUIDialog.setWindowOnTop(alertDialog, keyguard.isShowing())
-        if (QsDetailedView.isEnabled) {
-            alertDialog!!.show()
-        } else {
-            dialogTransitionAnimator.showFromDialog(alertDialog!!, internetDialog!!, null, false)
-        }
+
+        alertDialog!!.show()
     }
 
     private fun onClickConnectedWifi(view: View?) {
@@ -803,7 +774,6 @@
         secondaryMobileNetworkLayout?.setOnClickListener(null)
         seeAllLayout.setOnClickListener(null)
         wifiToggle.setOnCheckedChangeListener(null)
-        doneButton.setOnClickListener(null)
         shareWifiButton.setOnClickListener(null)
         airplaneModeButton.setOnClickListener(null)
         internetDetailsContentController.onStop()
@@ -825,8 +795,6 @@
     private fun getInternetContent(shouldUpdateMobileNetwork: Boolean): InternetContent {
         return InternetContent(
             shouldUpdateMobileNetwork = shouldUpdateMobileNetwork,
-            internetDialogTitleString = getDialogTitleText(),
-            internetDialogSubTitle = getSubtitleText(),
             activeNetworkIsCellular =
                 if (shouldUpdateMobileNetwork)
                     internetDetailsContentController.activeNetworkIsCellular()
@@ -924,10 +892,7 @@
                 if (DEBUG) {
                     Log.d(TAG, "dismissDialog")
                 }
-                if (internetDialog != null) {
-                    internetDialog!!.dismiss()
-                    internetDialog = null
-                }
+                // TODO: b/377388104 Close details view
             }
 
             override fun onAccessPointsChanged(
@@ -967,8 +932,6 @@
 
     @VisibleForTesting
     data class InternetContent(
-        val internetDialogTitleString: CharSequence,
-        val internetDialogSubTitle: CharSequence,
         val isAirplaneModeEnabled: Boolean = false,
         val hasEthernet: Boolean = false,
         val shouldUpdateMobileNetwork: Boolean = false,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
index f239a17..df4dddb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
@@ -16,44 +16,93 @@
 
 package com.android.systemui.qs.tiles.dialog
 
+import android.util.Log
 import android.view.LayoutInflater
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.viewinterop.AndroidView
 import com.android.systemui.plugins.qs.TileDetailsViewModel
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.connectivity.AccessPointController
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 
-class InternetDetailsViewModel(
-    onLongClick: () -> Unit,
+class InternetDetailsViewModel
+@AssistedInject
+constructor(
+    private val accessPointController: AccessPointController,
+    private val contentManagerFactory: InternetDetailsContentManager.Factory,
+    @Assisted private val onLongClick: () -> Unit,
 ) : TileDetailsViewModel() {
-    private val _onLongClick = onLongClick
+    private lateinit var internetDetailsContentManager: InternetDetailsContentManager
 
     @Composable
     override fun GetContentView() {
+        val coroutineScope = rememberCoroutineScope()
+        val context = LocalContext.current
+
+        internetDetailsContentManager = remember {
+            contentManagerFactory.create(
+                canConfigMobileData = accessPointController.canConfigMobileData(),
+                canConfigWifi = accessPointController.canConfigWifi(),
+                coroutineScope = coroutineScope,
+                context = context,
+            )
+        }
         AndroidView(
             modifier = Modifier.fillMaxWidth().fillMaxHeight(),
             factory = { context ->
-                // Inflate with the existing dialog xml layout
-                LayoutInflater.from(context)
-                    .inflate(R.layout.internet_connectivity_dialog, null)
-                // TODO: b/377388104 - Implement the internet details view
+                // Inflate with the existing dialog xml layout and bind it with the manager
+                val view =
+                    LayoutInflater.from(context)
+                        .inflate(R.layout.internet_connectivity_dialog, null)
+                internetDetailsContentManager.bind(view)
+
+                view
+                // TODO: b/377388104 - Polish the internet details view UI
+            },
+            onRelease = {
+                internetDetailsContentManager.unBind()
+                if (DEBUG) {
+                    Log.d(TAG, "onRelease")
+                }
             },
         )
     }
 
     override fun clickOnSettingsButton() {
-        _onLongClick()
+        onLongClick()
     }
 
     override fun getTitle(): String {
+        // TODO: b/377388104 make title and sub title mutable states of string
+        // by internetDetailsContentManager.getTitleText()
+        // TODO: test title change between airplane mode and not airplane mode
         // TODO: b/377388104 Update the placeholder text
         return "Internet"
     }
 
     override fun getSubTitle(): String {
+        // TODO: b/377388104 make title and sub title mutable states of string
+        // by internetDetailsContentManager.getSubtitleText()
+        // TODO: test subtitle change between airplane mode and not airplane mode
         // TODO: b/377388104 Update the placeholder text
         return "Tab a network to connect"
     }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(onLongClick: () -> Unit): InternetDetailsViewModel
+    }
+
+    companion object {
+        private const val TAG = "InternetDetailsVModel"
+        private val DEBUG: Boolean = Log.isLoggable(TAG, Log.DEBUG)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt
new file mode 100644
index 0000000..511597d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2025 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.qs.tiles.dialog
+
+import androidx.compose.runtime.Composable
+import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.statusbar.policy.ui.dialog.composable.ModeTileGrid
+import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
+
+/** The view model used for the modes details view in the Quick Settings */
+class ModesDetailsViewModel(
+    private val onSettingsClick: () -> Unit,
+    private val viewModel: ModesDialogViewModel,
+) : TileDetailsViewModel() {
+    @Composable
+    override fun GetContentView() {
+        // TODO(b/378513940): Finish implementing this function.
+        ModeTileGrid(viewModel = viewModel)
+    }
+
+    override fun clickOnSettingsButton() {
+        onSettingsClick()
+    }
+
+    override fun getTitle(): String {
+        // TODO(b/388321032): Replace this string with a string in a translatable xml file.
+        return "Modes"
+    }
+
+    override fun getSubTitle(): String {
+        // TODO(b/388321032): Replace this string with a string in a translatable xml file.
+        return "Silences interruptions from people and apps in different circumstances"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
index 4806c3f..0b0f2fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
 import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
@@ -31,7 +32,6 @@
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -47,7 +47,6 @@
 import kotlinx.coroutines.flow.shareIn
 
 @QSTileScope
-@OptIn(ExperimentalCoroutinesApi::class)
 class CustomTileDataInteractor
 @Inject
 constructor(
@@ -58,6 +57,7 @@
     private val packageUpdatesRepository: CustomTilePackageUpdatesRepository,
     userRepository: UserRepository,
     @QSTileScope private val tileScope: CoroutineScope,
+    qsTileLogger: QSTileLogger,
 ) : QSTileDataInteractor<CustomTileDataModel> {
 
     private val mutableUserFlow = MutableStateFlow(userRepository.getSelectedUserInfo().userHandle)
@@ -71,6 +71,7 @@
                     // binding the service might access it
                     customTileInteractor.initForUser(user)
                     // Bind the TileService for not active tile
+                    qsTileLogger.logInfo(tileSpec, "onBindingFlow for user:$user")
                     serviceInteractor.bindOnStart()
 
                     packageUpdatesRepository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
index ccc84c0..c0fc93f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
@@ -25,6 +25,7 @@
 import android.service.quicksettings.IQSTileService
 import android.service.quicksettings.Tile
 import android.service.quicksettings.TileService
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.external.CustomTileInterface
 import com.android.systemui.qs.external.TileServiceManager
@@ -36,20 +37,17 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.channels.produce
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
  * Communicates with [TileService] via [TileServiceManager] and [IQSTileService]. This interactor is
  * also responsible for the binding to the [TileService].
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @QSTileScope
 class CustomTileServiceInteractor
 @Inject
@@ -74,6 +72,7 @@
 
     val callingAppIds: Flow<Int>
         get() = tileReceivingInterface.mutableCallingAppIds
+
     val refreshEvents: Flow<Unit>
         get() = tileReceivingInterface.mutableRefreshEvents
 
@@ -146,6 +145,7 @@
 
     private fun getTileServiceManager(): TileServiceManager =
         synchronized(tileServices) {
+            qsTileLogger.logInfo(tileSpec, "getTileServiceManager called")
             if (tileServiceManager == null) {
                 tileServices
                     .getTileWrapper(tileReceivingInterface)
@@ -175,8 +175,10 @@
 
         override val user: Int
             get() = currentUser.identifier
+
         override val qsTile: Tile
             get() = customTileInteractor.getTile(currentUser)
+
         override val component: ComponentName = tileSpec.componentName
 
         val mutableCallingAppIds = MutableStateFlow(Process.INVALID_UID)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
index 6d10843..871c051 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
@@ -41,7 +41,6 @@
 import com.android.systemui.utils.coroutines.flow.mapLatestConflated
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -50,7 +49,6 @@
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
 
-@OptIn(ExperimentalCoroutinesApi::class)
 /** Observes internet state changes providing the [InternetTileModel]. */
 class InternetTileDataInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
index c4f9515..6e2c437 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
@@ -43,6 +43,7 @@
     private val wifiStateWorker: WifiStateWorker,
     private val accessPointController: AccessPointController,
     private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+    private val internetDetailsViewModelFactory: InternetDetailsViewModel.Factory,
 ) : QSTileUserActionInteractor<InternetTileModel> {
 
     override suspend fun handleInput(input: QSTileInput<InternetTileModel>): Unit =
@@ -70,7 +71,7 @@
         }
 
     override val detailsViewModel: TileDetailsViewModel =
-        InternetDetailsViewModel { handleLongClick(null) }
+        internetDetailsViewModelFactory.create { handleLongClick(null) }
 
     private fun handleLongClick(expandable:Expandable?){
         qsTileIntentUserActionHandler.handle(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
index 5ce7f0d..b5da044 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
@@ -54,7 +54,7 @@
                     handleToggleClick(input.data)
                 }
                 is QSTileUserAction.LongClick -> {
-                    qsTileIntentUserInputHandler.handle(action.expandable, longClickIntent)
+                    handleLongClick(action.expandable)
                 }
             }
         }
@@ -95,6 +95,10 @@
         }
     }
 
+    fun handleLongClick(expandable: Expandable?) {
+        qsTileIntentUserInputHandler.handle(expandable, longClickIntent)
+    }
+
     companion object {
         const val TAG = "ModesTileUserActionInteractor"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
index 000f7f8..5bc26f5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -21,7 +21,8 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
 import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
@@ -47,7 +48,11 @@
                     }
                     put(
                         Swipe.Down(fromSource = SceneContainerEdge.TopLeft),
-                        ReplaceByOverlay(Overlays.NotificationsShade),
+                        ShowOverlay(
+                            Overlays.NotificationsShade,
+                            hideCurrentOverlays =
+                                HideCurrentOverlays.Some(Overlays.QuickSettingsShade),
+                        ),
                     )
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
rename to packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
index 60c2cca..9af4630 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
@@ -100,14 +100,14 @@
 import com.android.systemui.navigationbar.views.NavigationBarView;
 import com.android.systemui.navigationbar.views.buttons.KeyButtonView;
 import com.android.systemui.process.ProcessWrapper;
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener;
 import com.android.systemui.scene.domain.interactor.SceneInteractor;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ILauncherProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -135,16 +135,16 @@
 import javax.inject.Provider;
 
 /**
- * Class to send information from overview to launcher with a binder.
+ * Class to send information from SysUI to Launcher with a binder.
  */
 @SysUISingleton
-public class OverviewProxyService implements CallbackController<OverviewProxyListener>,
+public class LauncherProxyService implements CallbackController<LauncherProxyListener>,
         NavigationModeController.ModeChangedListener, Dumpable {
 
     @VisibleForTesting
     static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
 
-    public static final String TAG_OPS = "OverviewProxyService";
+    public static final String TAG_OPS = "LauncherProxyService";
     private static final long BACKOFF_MILLIS = 1000;
     private static final long DEFERRED_CALLBACK_MILLIS = 5000;
     // Max backoff caps at 5 mins
@@ -165,7 +165,7 @@
     private final Runnable mConnectionRunnable = () ->
             internalConnectToCurrentUser("runnable: startConnectionToCurrentUser");
     private final ComponentName mRecentsComponentName;
-    private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
+    private final List<LauncherProxyListener> mConnectionCallbacks = new ArrayList<>();
     private final Intent mQuickStepIntent;
     private final ScreenshotHelper mScreenshotHelper;
     private final CommandQueue mCommandQueue;
@@ -179,12 +179,12 @@
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final BackAnimation mBackAnimation;
 
-    private IOverviewProxy mOverviewProxy;
+    private ILauncherProxy mLauncherProxy;
     private int mConnectionBackoffAttempts;
     private boolean mBound;
     private boolean mIsEnabled;
-    // This is set to false when the overview service is requested to be bound until it is notified
-    // that the previous service has been cleaned up in IOverviewProxy#onUnbind(). It is also set to
+    // This is set to false when the launcher service is requested to be bound until it is notified
+    // that the previous service has been cleaned up in ILauncherProxy#onUnbind(). It is also set to
     // true after a 1000ms timeout by mDeferredBindAfterTimedOutCleanup.
     private boolean mIsPrevServiceCleanedUp = true;
 
@@ -341,7 +341,7 @@
         @Override
         public void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) {
             verifyCallerAndClearCallingIdentityPostMain("updateContextualEduStats",
-                    () -> mHandler.post(() -> OverviewProxyService.this.updateContextualEduStats(
+                    () -> mHandler.post(() -> LauncherProxyService.this.updateContextualEduStats(
                             isTrackpadGesture, GestureType.valueOf(gestureType))));
         }
 
@@ -504,7 +504,7 @@
         public void onReceive(Context context, Intent intent) {
             if (Objects.equals(intent.getAction(), Intent.ACTION_USER_UNLOCKED)) {
                 if (keyguardPrivateNotifications()) {
-                    // Start the overview connection to the launcher service
+                    // Start the launcher connection to the launcher service
                     // Connect if user hasn't connected yet
                     if (getProxy() == null) {
                         startConnectionToCurrentUser();
@@ -546,14 +546,14 @@
         }
     };
 
-    private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
+    private final ServiceConnection mLauncherServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            Log.d(TAG_OPS, "Overview proxy service connected");
+            Log.d(TAG_OPS, "Launcher proxy service connected");
             mConnectionBackoffAttempts = 0;
             mHandler.removeCallbacks(mDeferredConnectionCallback);
             try {
-                service.linkToDeath(mOverviewServiceDeathRcpt, 0);
+                service.linkToDeath(mLauncherServiceDeathRcpt, 0);
             } catch (RemoteException e) {
                 // Failed to link to death (process may have died between binding and connecting),
                 // just unbind the service for now and retry again
@@ -564,7 +564,7 @@
             }
 
             mCurrentBoundedUserId = mUserTracker.getUserId();
-            mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
+            mLauncherProxy = ILauncherProxy.Stub.asInterface(service);
 
             Bundle params = new Bundle();
             addInterface(mSysUiProxy, params);
@@ -574,8 +574,8 @@
             mShellInterface.createExternalInterfaces(params);
 
             try {
-                Log.d(TAG_OPS, "OverviewProxyService connected, initializing overview proxy");
-                mOverviewProxy.onInitialize(params);
+                Log.d(TAG_OPS, "LauncherProxyService connected, initializing launcher proxy");
+                mLauncherProxy.onInitialize(params);
             } catch (RemoteException e) {
                 mCurrentBoundedUserId = -1;
                 Log.e(TAG_OPS, "Failed to call onInitialize()", e);
@@ -614,7 +614,7 @@
     private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
 
     // This is the death handler for the binder from the launcher service
-    private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
+    private final IBinder.DeathRecipient mLauncherServiceDeathRcpt
             = this::cleanupAfterDeath;
 
     private final IVoiceInteractionSessionListener mVoiceInteractionSessionListener =
@@ -632,7 +632,7 @@
         @Override
         public void onVoiceSessionWindowVisibilityChanged(boolean visible) {
             mContext.getMainExecutor().execute(() ->
-                    OverviewProxyService.this.onVoiceSessionWindowVisibilityChanged(visible));
+                    LauncherProxyService.this.onVoiceSessionWindowVisibilityChanged(visible));
         }
 
         @Override
@@ -652,7 +652,7 @@
 
     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
     @Inject
-    public OverviewProxyService(Context context,
+    public LauncherProxyService(Context context,
             @Main Executor mainExecutor,
             CommandQueue commandQueue,
             ShellInterface shellInterface,
@@ -755,14 +755,14 @@
 
             @Override
             public void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop) {
-                if (mOverviewProxy != null) {
+                if (mLauncherProxy != null) {
                     try {
                         if (DesktopModeStatus.canEnterDesktopMode(mContext)
                                 && (sysUiState.getFlags()
                                 & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0) {
                             return;
                         }
-                        mOverviewProxy.enterStageSplitFromRunningApp(leftOrTop);
+                        mLauncherProxy.enterStageSplitFromRunningApp(leftOrTop);
                     } catch (RemoteException e) {
                         Log.w(TAG_OPS, "Unable to enter stage split from the current running app");
                     }
@@ -817,12 +817,12 @@
 
     private void notifySystemUiStateFlags(@SystemUiStateFlags long flags) {
         if (SysUiState.DEBUG) {
-            Log.d(TAG_OPS, "Notifying sysui state change to overview service: proxy="
-                    + mOverviewProxy + " flags=" + flags);
+            Log.d(TAG_OPS, "Notifying sysui state change to launcher service: proxy="
+                    + mLauncherProxy + " flags=" + flags);
         }
         try {
-            if (mOverviewProxy != null) {
-                mOverviewProxy.onSystemUiStateChanged(flags);
+            if (mLauncherProxy != null) {
+                mLauncherProxy.onSystemUiStateChanged(flags);
             }
         } catch (RemoteException e) {
             Log.e(TAG_OPS, "Failed to notify sysui state change", e);
@@ -854,9 +854,9 @@
     }
 
     private void dispatchNavButtonBounds() {
-        if (mOverviewProxy != null && mActiveNavBarRegion != null) {
+        if (mLauncherProxy != null && mActiveNavBarRegion != null) {
             try {
-                mOverviewProxy.onActiveNavBarRegionChanges(mActiveNavBarRegion);
+                mLauncherProxy.onActiveNavBarRegionChanges(mActiveNavBarRegion);
             } catch (RemoteException e) {
                 Log.e(TAG_OPS, "Failed to call onActiveNavBarRegionChanges()", e);
             }
@@ -888,7 +888,7 @@
             // This should not happen, but if any per-user SysUI component has a dependency on OPS,
             // then this could get triggered
             Log.w(TAG_OPS,
-                    "Skipping connection to overview service due to non-system foreground user "
+                    "Skipping connection to launcher service due to non-system foreground user "
                             + "caller");
             return;
         }
@@ -925,7 +925,7 @@
         }
         try {
             mBound = mContext.bindServiceAsUser(mQuickStepIntent,
-                    mOverviewServiceConnection,
+                    mLauncherServiceConnection,
                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                     currentUser);
         } catch (SecurityException e) {
@@ -954,15 +954,15 @@
     }
 
     @Override
-    public void addCallback(@NonNull OverviewProxyListener listener) {
+    public void addCallback(@NonNull LauncherProxyListener listener) {
         if (!mConnectionCallbacks.contains(listener)) {
             mConnectionCallbacks.add(listener);
         }
-        listener.onConnectionChanged(mOverviewProxy != null);
+        listener.onConnectionChanged(mLauncherProxy != null);
     }
 
     @Override
-    public void removeCallback(@NonNull OverviewProxyListener listener) {
+    public void removeCallback(@NonNull LauncherProxyListener listener) {
         mConnectionCallbacks.remove(listener);
     }
 
@@ -974,21 +974,21 @@
         return mIsEnabled;
     }
 
-    public IOverviewProxy getProxy() {
-        return mOverviewProxy;
+    public ILauncherProxy getProxy() {
+        return mLauncherProxy;
     }
 
     private void disconnectFromLauncherService(String disconnectReason) {
         Log.d(TAG_OPS, "disconnectFromLauncherService bound?: " + mBound +
-                " currentProxy: " + mOverviewProxy + " disconnectReason: " + disconnectReason,
+                " currentProxy: " + mLauncherProxy + " disconnectReason: " + disconnectReason,
                 new Throwable());
         if (mBound) {
             // Always unbind the service (ie. if called through onNullBinding or onBindingDied)
-            mContext.unbindService(mOverviewServiceConnection);
+            mContext.unbindService(mLauncherServiceConnection);
             mBound = false;
-            if (mOverviewProxy != null) {
+            if (mLauncherProxy != null) {
                 try {
-                    mOverviewProxy.onUnbind(new IRemoteCallback.Stub() {
+                    mLauncherProxy.onUnbind(new IRemoteCallback.Stub() {
                         @Override
                         public void sendResult(Bundle data) throws RemoteException {
                             // Received Launcher reply, try to bind anew.
@@ -1006,9 +1006,9 @@
             }
         }
 
-        if (mOverviewProxy != null) {
-            mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
-            mOverviewProxy = null;
+        if (mLauncherProxy != null) {
+            mLauncherProxy.asBinder().unlinkToDeath(mLauncherServiceDeathRcpt, 0);
+            mLauncherProxy = null;
             notifyConnectionChanged();
         }
     }
@@ -1044,7 +1044,7 @@
 
     private void notifyConnectionChanged() {
         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
-            mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
+            mConnectionCallbacks.get(i).onConnectionChanged(mLauncherProxy != null);
         }
     }
 
@@ -1095,10 +1095,10 @@
 
     public void notifyAssistantVisibilityChanged(float visibility) {
         try {
-            if (mOverviewProxy != null) {
-                mOverviewProxy.onAssistantVisibilityChanged(visibility);
+            if (mLauncherProxy != null) {
+                mLauncherProxy.onAssistantVisibilityChanged(visibility);
             } else {
-                Log.e(TAG_OPS, "Failed to get overview proxy for assistant visibility.");
+                Log.e(TAG_OPS, "Failed to get launcher proxy for assistant visibility.");
             }
         } catch (RemoteException e) {
             Log.e(TAG_OPS, "Failed to call notifyAssistantVisibilityChanged()", e);
@@ -1148,10 +1148,10 @@
 
     public void disable(int displayId, int state1, int state2, boolean animate) {
         try {
-            if (mOverviewProxy != null) {
-                mOverviewProxy.disable(displayId, state1, state2, animate);
+            if (mLauncherProxy != null) {
+                mLauncherProxy.disable(displayId, state1, state2, animate);
             } else {
-                Log.e(TAG_OPS, "Failed to get overview proxy for disable flags.");
+                Log.e(TAG_OPS, "Failed to get launcher proxy for disable flags.");
             }
         } catch (RemoteException e) {
             Log.e(TAG_OPS, "Failed to call disable()", e);
@@ -1160,10 +1160,10 @@
 
     public void onRotationProposal(int rotation, boolean isValid) {
         try {
-            if (mOverviewProxy != null) {
-                mOverviewProxy.onRotationProposal(rotation, isValid);
+            if (mLauncherProxy != null) {
+                mLauncherProxy.onRotationProposal(rotation, isValid);
             } else {
-                Log.e(TAG_OPS, "Failed to get overview proxy for proposing rotation.");
+                Log.e(TAG_OPS, "Failed to get launcher proxy for proposing rotation.");
             }
         } catch (RemoteException e) {
             Log.e(TAG_OPS, "Failed to call onRotationProposal()", e);
@@ -1172,10 +1172,10 @@
 
     public void onSystemBarAttributesChanged(int displayId, int behavior) {
         try {
-            if (mOverviewProxy != null) {
-                mOverviewProxy.onSystemBarAttributesChanged(displayId, behavior);
+            if (mLauncherProxy != null) {
+                mLauncherProxy.onSystemBarAttributesChanged(displayId, behavior);
             } else {
-                Log.e(TAG_OPS, "Failed to get overview proxy for system bar attr change.");
+                Log.e(TAG_OPS, "Failed to get launcher proxy for system bar attr change.");
             }
         } catch (RemoteException e) {
             Log.e(TAG_OPS, "Failed to call onSystemBarAttributesChanged()", e);
@@ -1184,10 +1184,10 @@
 
     public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
         try {
-            if (mOverviewProxy != null) {
-                mOverviewProxy.onNavButtonsDarkIntensityChanged(darkIntensity);
+            if (mLauncherProxy != null) {
+                mLauncherProxy.onNavButtonsDarkIntensityChanged(darkIntensity);
             } else {
-                Log.e(TAG_OPS, "Failed to get overview proxy to update nav buttons dark intensity");
+                Log.e(TAG_OPS, "Failed to get launcher proxy to update nav buttons dark intensity");
             }
         } catch (RemoteException e) {
             Log.e(TAG_OPS, "Failed to call onNavButtonsDarkIntensityChanged()", e);
@@ -1196,10 +1196,10 @@
 
     public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
         try {
-            if (mOverviewProxy != null) {
-                mOverviewProxy.onNavigationBarLumaSamplingEnabled(displayId, enable);
+            if (mLauncherProxy != null) {
+                mLauncherProxy.onNavigationBarLumaSamplingEnabled(displayId, enable);
             } else {
-                Log.e(TAG_OPS, "Failed to get overview proxy to enable/disable nav bar luma"
+                Log.e(TAG_OPS, "Failed to get launcher proxy to enable/disable nav bar luma"
                         + "sampling");
             }
         } catch (RemoteException e) {
@@ -1221,7 +1221,7 @@
     @Override
     public void dump(PrintWriter pw, String[] args) {
         pw.println(TAG_OPS + " state:");
-        pw.print("  isConnected="); pw.println(mOverviewProxy != null);
+        pw.print("  isConnected="); pw.println(mLauncherProxy != null);
         pw.print("  mIsEnabled="); pw.println(isEnabled());
         pw.print("  mRecentsComponentName="); pw.println(mRecentsComponentName);
         pw.print("  mQuickStepIntent="); pw.println(mQuickStepIntent);
@@ -1237,7 +1237,7 @@
         mSysUiState.dump(pw, args);
     }
 
-    public interface OverviewProxyListener {
+    public interface LauncherProxyListener {
         default void onConnectionChanged(boolean isConnected) {}
         default void onPrioritizedRotation(@Surface.Rotation int rotation) {}
         default void onOverviewShown(boolean fromHome) {}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 21c5ae8..e51b73d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -23,30 +23,30 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ILauncherProxy;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import javax.inject.Inject;
 
 /**
- * An implementation of the Recents interface which proxies to the OverviewProxyService.
+ * An implementation of the Recents interface which proxies to the LauncherProxyService.
  */
 @SysUISingleton
 public class OverviewProxyRecentsImpl implements RecentsImplementation {
 
     private final static String TAG = "OverviewProxyRecentsImpl";
     private Handler mHandler;
-    private final OverviewProxyService mOverviewProxyService;
+    private final LauncherProxyService mLauncherProxyService;
     private final ActivityStarter mActivityStarter;
     private final KeyguardStateController mKeyguardStateController;
 
     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
     @Inject
     public OverviewProxyRecentsImpl(
-            OverviewProxyService overviewProxyService,
+            LauncherProxyService launcherProxyService,
             ActivityStarter activityStarter,
             KeyguardStateController keyguardStateController) {
-        mOverviewProxyService = overviewProxyService;
+        mLauncherProxyService = launcherProxyService;
         mActivityStarter = activityStarter;
         mKeyguardStateController = keyguardStateController;
     }
@@ -58,10 +58,10 @@
 
     @Override
     public void showRecentApps(boolean triggeredFromAltTab) {
-        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
-        if (overviewProxy != null) {
+        ILauncherProxy launcherProxy = mLauncherProxyService.getProxy();
+        if (launcherProxy != null) {
             try {
-                overviewProxy.onOverviewShown(triggeredFromAltTab);
+                launcherProxy.onOverviewShown(triggeredFromAltTab);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to send overview show event to launcher.", e);
             }
@@ -70,10 +70,10 @@
 
     @Override
     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
-        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
-        if (overviewProxy != null) {
+        ILauncherProxy launcherProxy = mLauncherProxyService.getProxy();
+        if (launcherProxy != null) {
             try {
-                overviewProxy.onOverviewHidden(triggeredFromAltTab, triggeredFromHomeKey);
+                launcherProxy.onOverviewHidden(triggeredFromAltTab, triggeredFromHomeKey);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to send overview hide event to launcher.", e);
             }
@@ -83,13 +83,13 @@
     @Override
     public void toggleRecentApps() {
         // If connected to launcher service, let it handle the toggle logic
-        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
-        if (overviewProxy != null) {
+        ILauncherProxy launcherProxy = mLauncherProxyService.getProxy();
+        if (launcherProxy != null) {
             final Runnable toggleRecents = () -> {
                 try {
-                    if (mOverviewProxyService.getProxy() != null) {
-                        mOverviewProxyService.getProxy().onOverviewToggle();
-                        mOverviewProxyService.notifyToggleRecentApps();
+                    if (mLauncherProxyService.getProxy() != null) {
+                        mLauncherProxyService.getProxy().onOverviewToggle();
+                        mLauncherProxyService.notifyToggleRecentApps();
                     }
                 } catch (RemoteException e) {
                     Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index 0488962..80c7c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.scene.data.repository
 
 import com.android.compose.animation.scene.ContentKey
@@ -29,7 +27,6 @@
 import com.android.systemui.scene.shared.model.SceneDataSource
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
index be792df..f2f237a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
@@ -16,13 +16,27 @@
 
 package com.android.systemui.scene.domain
 
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
 import com.android.systemui.scene.domain.resolver.SceneResolverModule
 import dagger.Module
+import dagger.Provides
+import javax.inject.Qualifier
 
-@Module(
-    includes =
-        [
-            SceneResolverModule::class,
-        ]
-)
-object SceneDomainModule
+@Module(includes = [SceneResolverModule::class])
+object SceneDomainModule {
+
+    @JvmStatic
+    @Provides
+    @SysUISingleton
+    @SceneFrameworkTableLog
+    fun provideSceneFrameworkTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
+        return factory.create("SceneFrameworkTableLog", 100)
+    }
+}
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class SceneFrameworkTableLog
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
index bebd398..c9d8e02 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
@@ -18,11 +18,16 @@
 
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableRowLogger
 import com.android.systemui.scene.data.model.SceneStack
+import com.android.systemui.scene.data.model.asIterable
 import com.android.systemui.scene.data.model.peek
 import com.android.systemui.scene.data.model.pop
 import com.android.systemui.scene.data.model.push
 import com.android.systemui.scene.data.model.sceneStackOf
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
 import com.android.systemui.scene.shared.logger.SceneLogger
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import javax.inject.Inject
@@ -39,6 +44,7 @@
 constructor(
     private val logger: SceneLogger,
     private val sceneContainerConfig: SceneContainerConfig,
+    @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
 ) {
     private val _backStack = MutableStateFlow(sceneStackOf())
     val backStack: StateFlow<SceneStack> = _backStack.asStateFlow()
@@ -58,6 +64,7 @@
     fun onSceneChange(from: SceneKey, to: SceneKey) {
         check(from != to) { "from == to, from=${from.debugName}, to=${to.debugName}" }
 
+        val prevVal = backStack.value
         _backStack.update { stack ->
             when (stackOperation(from, to, stack)) {
                 null -> stack
@@ -68,12 +75,21 @@
             }
         }
         logger.logSceneBackStack(backStack.value)
+        tableLogBuffer.logDiffs(
+            prevVal = DiffableSceneStack(prevVal),
+            newVal = DiffableSceneStack(backStack.value),
+        )
     }
 
     /** Applies the given [transform] to the back stack. */
     fun updateBackStack(transform: (SceneStack) -> SceneStack) {
+        val prevVal = backStack.value
         _backStack.update { stack -> transform(stack) }
         logger.logSceneBackStack(backStack.value)
+        tableLogBuffer.logDiffs(
+            prevVal = DiffableSceneStack(prevVal),
+            newVal = DiffableSceneStack(backStack.value),
+        )
     }
 
     private fun stackOperation(from: SceneKey, to: SceneKey, stack: SceneStack): StackOperation? {
@@ -106,4 +122,15 @@
     private data object Push : StackOperation
 
     private data object Pop : StackOperation
+
+    private class DiffableSceneStack(private val sceneStack: SceneStack) :
+        Diffable<DiffableSceneStack> {
+
+        override fun logDiffs(prevVal: DiffableSceneStack, row: TableRowLogger) {
+            row.logChange(
+                columnName = "backStack",
+                value = sceneStack.asIterable().joinToString { it.debugName },
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index ba9dc76..9c04323 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -27,15 +27,19 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableRowLogger
 import com.android.systemui.scene.data.repository.SceneContainerRepository
 import com.android.systemui.scene.domain.resolver.SceneResolver
 import com.android.systemui.scene.shared.logger.SceneLogger
 import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.pairwise
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -48,6 +52,7 @@
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
 
 /**
  * Generic business logic and app state accessors for the scene framework.
@@ -56,7 +61,6 @@
  * other feature modules should depend on and call into this class when their parts of the
  * application state change.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class SceneInteractor
 @Inject
@@ -149,7 +153,6 @@
      * their finger to transition between scenes, this value will be true while their finger is on
      * the screen, then false for the rest of the transition.
      */
-    @OptIn(ExperimentalCoroutinesApi::class)
     val isTransitionUserInputOngoing: StateFlow<Boolean> =
         transitionState
             .flatMapLatest {
@@ -565,6 +568,28 @@
         decrementActiveTransitionAnimationCount()
     }
 
+    suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+        coroutineScope {
+            launch {
+                currentScene
+                    .map { sceneKey -> DiffableSceneKey(key = sceneKey) }
+                    .pairwise()
+                    .collect { (prev, current) ->
+                        tableLogBuffer.logDiffs(prevVal = prev, newVal = current)
+                    }
+            }
+
+            launch {
+                currentOverlays
+                    .map { overlayKeys -> DiffableOverlayKeys(keys = overlayKeys) }
+                    .pairwise()
+                    .collect { (prev, current) ->
+                        tableLogBuffer.logDiffs(prevVal = prev, newVal = current)
+                    }
+            }
+        }
+    }
+
     private fun decrementActiveTransitionAnimationCount() {
         repository.activeTransitionAnimationCount.update { current ->
             (current - 1).also {
@@ -576,4 +601,20 @@
             }
         }
     }
+
+    private class DiffableSceneKey(private val key: SceneKey) : Diffable<DiffableSceneKey> {
+        override fun logDiffs(prevVal: DiffableSceneKey, row: TableRowLogger) {
+            row.logChange(columnName = "currentScene", value = key.debugName)
+        }
+    }
+
+    private class DiffableOverlayKeys(private val keys: Set<OverlayKey>) :
+        Diffable<DiffableOverlayKeys> {
+        override fun logDiffs(prevVal: DiffableOverlayKeys, row: TableRowLogger) {
+            row.logChange(
+                columnName = "currentOverlays",
+                value = keys.joinToString { key -> key.debugName },
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
index 3a07ce9..7922613 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
@@ -36,7 +36,6 @@
 import javax.inject.Inject
 import javax.inject.Provider
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
@@ -47,7 +46,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** Business logic about the visibility of various parts of the window root view. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class WindowRootViewVisibilityInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
index 6e79d56..140b231 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.scene.domain.resolver
 
 import android.util.Log
@@ -33,7 +31,6 @@
 import dagger.multibindings.IntoSet
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.stateIn
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt
index 7939404..4044381 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.scene.domain.startable
 
 import android.os.DeadObjectException
@@ -35,7 +33,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index ecd0027..2fd5841 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.scene.domain.startable
 
 import android.app.StatusBarManager
@@ -47,6 +45,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.keyguardScenes
+import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.model.SceneContainerPlugin
 import com.android.systemui.model.SysUiState
 import com.android.systemui.model.updateFlags
@@ -56,6 +55,7 @@
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.scene.data.model.asIterable
 import com.android.systemui.scene.data.model.sceneStackOf
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
 import com.android.systemui.scene.domain.interactor.DisabledContentInteractor
 import com.android.systemui.scene.domain.interactor.SceneBackInteractor
 import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
@@ -67,6 +67,7 @@
 import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.VibratorHelper
@@ -86,7 +87,6 @@
 import java.util.Optional
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.SharingStarted
@@ -109,7 +109,6 @@
  * Hooks up business logic that manipulates the state of the [SceneInteractor] for the system UI
  * scene container based on state from other systems.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class SceneContainerStartable
 @Inject
@@ -147,6 +146,8 @@
     private val msdlPlayer: MSDLPlayer,
     private val disabledContentInteractor: DisabledContentInteractor,
     private val activityTransitionAnimator: ActivityTransitionAnimator,
+    private val shadeModeInteractor: ShadeModeInteractor,
+    @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
 ) : CoreStartable {
     private val centralSurfaces: CentralSurfaces?
         get() = centralSurfacesOptLazy.get().getOrNull()
@@ -156,6 +157,7 @@
     override fun start() {
         if (SceneContainerFlag.isEnabled) {
             sceneLogger.logFrameworkEnabled(isEnabled = true)
+            applicationScope.launch { hydrateTableLogBuffer() }
             hydrateVisibility()
             automaticallySwitchScenes()
             hydrateSystemUiState()
@@ -179,15 +181,62 @@
         }
     }
 
-    override fun dump(pw: PrintWriter, args: Array<out String>) =
-        pw.asIndenting().run {
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        with(pw.asIndenting()) {
             printSection("SceneContainerFlag") {
-                println("isEnabled", SceneContainerFlag.isEnabled)
-                printSection("requirementDescription") {
+                printSection("Framework availability") {
+                    println("isEnabled", SceneContainerFlag.isEnabled)
                     println(SceneContainerFlag.requirementDescription())
                 }
+
+                if (!SceneContainerFlag.isEnabled) {
+                    return
+                }
+
+                printSection("Scene state") {
+                    println("currentScene", sceneInteractor.currentScene.value.debugName)
+                    println(
+                        "currentOverlays",
+                        sceneInteractor.currentOverlays.value.joinToString(", ") { overlay ->
+                            overlay.debugName
+                        },
+                    )
+                    println("backStack", sceneBackInteractor.backStack.value)
+                    println("shadeMode", shadeModeInteractor.shadeMode.value)
+                }
+
+                printSection("Authentication state") {
+                    println("isKeyguardEnabled", keyguardEnabledInteractor.isKeyguardEnabled.value)
+                    println(
+                        "isUnlocked",
+                        deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked,
+                    )
+                    println("isDeviceEntered", deviceEntryInteractor.isDeviceEntered.value)
+                    println(
+                        "isFaceAuthEnabledAndEnrolled",
+                        faceUnlockInteractor.isFaceAuthEnabledAndEnrolled(),
+                    )
+                    println("canSwipeToEnter", deviceEntryInteractor.canSwipeToEnter.value)
+                }
+
+                printSection("Power state") {
+                    println("detailedWakefulness", powerInteractor.detailedWakefulness.value)
+                    println("isDozing", keyguardInteractor.isDozing.value)
+                    println("isAodAvailable", keyguardInteractor.isAodAvailable.value)
+                }
             }
         }
+    }
+
+    private suspend fun hydrateTableLogBuffer() {
+        coroutineScope {
+            launch { sceneInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+            launch { keyguardEnabledInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+            launch { faceUnlockInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+            launch { powerInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+            launch { keyguardInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+        }
+    }
 
     private fun resetShadeSessions() {
         applicationScope.launch {
@@ -945,10 +994,6 @@
                 override fun onTransitionAnimationEnd() {
                     sceneInteractor.onTransitionAnimationEnd()
                 }
-
-                override fun onTransitionAnimationCancelled() {
-                    sceneInteractor.onTransitionAnimationCancelled()
-                }
             }
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
index 1d97034..522dfab 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.scene.domain.startable
 
 import androidx.annotation.VisibleForTesting
@@ -45,7 +43,6 @@
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
index 1d8dc4f..7d09c45 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.scene.domain.startable
 
 import android.annotation.SuppressLint
@@ -50,7 +48,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import com.android.app.tracing.coroutines.launchTraced as launch
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index 2bcd1d1..733b421 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -89,8 +89,11 @@
     @JvmStatic
     fun requirementDescription(): String {
         return buildString {
-            getAllRequirements().forEach { requirement ->
-                append('\n')
+            getAllRequirements().forEachIndexed { index, requirement ->
+                if (index > 0) {
+                    append('\n')
+                }
+
                 append(if (requirement.isEnabled) "    [MET]" else "[NOT MET]")
                 append(" ${requirement.name}")
             }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt
index eb4c0f2..5d0edc5 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt
@@ -14,15 +14,12 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.scene.shared.model
 
 import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index b8da227..0dd0e3d 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -14,11 +14,9 @@
 import com.android.systemui.shade.TouchLogger
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
 import javax.inject.Provider
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 
 /** A root view of the main SysUI window that supports scenes. */
-@ExperimentalCoroutinesApi
 class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootView(context, attrs) {
 
     private var motionEventHandler: SceneContainerViewModel.MotionEventHandler? = null
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 7da007c..ffc8268 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -51,14 +51,12 @@
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
 import javax.inject.Provider
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
-@ExperimentalCoroutinesApi
 object SceneWindowRootViewBinder {
 
     /** Binds between the view and view-model pertaining to a specific scene container. */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
index 08214c4..f5c6052 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
@@ -35,7 +35,6 @@
 import android.view.Display
 import android.view.ScrollCaptureResponse
 import android.view.ViewRootImpl.ActivityConfigCallback
-import android.view.WindowManager
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
 import android.widget.Toast
 import android.window.WindowContext
@@ -218,9 +217,7 @@
         window.setFocusable(true)
         viewProxy.requestFocus()
 
-        if (screenshot.type != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
-            enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
-        }
+        enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
 
         window.attachWindow()
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt
index 2e6c756..d82a8bd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt
@@ -31,7 +31,7 @@
 
     private val mBinder: IBinder =
         object : ICrossProfileService.Stub() {
-            override fun launchIntent(intent: Intent, bundle: Bundle) {
+            override fun launchIntent(intent: Intent, bundle: Bundle?) {
                 startActivity(intent, bundle)
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 49f3cfc..917869a6 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -27,6 +27,8 @@
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.os.Looper;
 import android.util.AttributeSet;
@@ -373,4 +375,18 @@
             ((ScrimDrawable) mDrawable).setRoundedCorners(radius);
         }
     }
+
+    /**
+     * Blur the view with the specific blur radius or clear any blurs if the radius is 0
+     */
+    public void setBlurRadius(float blurRadius) {
+        if (blurRadius > 0) {
+            setRenderEffect(RenderEffect.createBlurEffect(
+                    blurRadius,
+                    blurRadius,
+                    Shader.TileMode.CLAMP));
+        } else {
+            setRenderEffect(null);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 6844f05..eae0ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.settings.brightness;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.Intent.EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -157,6 +159,7 @@
     }
 
     void setBrightnessDialogViewAttributes(View container) {
+        Configuration configuration = getResources().getConfiguration();
         // The brightness mirror container is INVISIBLE by default.
         container.setVisibility(View.VISIBLE);
         ViewGroup.MarginLayoutParams lp =
@@ -171,9 +174,16 @@
                         R.dimen.notification_guts_option_vertical_padding);
 
         lp.topMargin = verticalMargin;
+        // If in multi-window or freeform, increase the top margin so the brightness dialog
+        // doesn't get cut off.
+        final int windowingMode = configuration.windowConfiguration.getWindowingMode();
+        if (windowingMode == WINDOWING_MODE_MULTI_WINDOW
+                || windowingMode == WINDOWING_MODE_FREEFORM) {
+            lp.topMargin += 50;
+        }
+
         lp.bottomMargin = verticalMargin;
 
-        Configuration configuration = getResources().getConfiguration();
         int orientation = configuration.orientation;
         int windowWidth = getWindowAvailableWidth();
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/BaseShadeControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/BaseShadeControllerImpl.kt
index 2fdcaa5..b271c69 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/BaseShadeControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/BaseShadeControllerImpl.kt
@@ -22,10 +22,8 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import dagger.Lazy
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** A base class for non-empty implementations of ShadeController. */
-@OptIn(ExperimentalCoroutinesApi::class)
 abstract class BaseShadeControllerImpl(
     protected val commandQueue: CommandQueue,
     protected val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 19bf4c0..28f5694 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -109,7 +109,6 @@
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
-import com.android.systemui.keyguard.ui.transitions.BlurConfig;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel;
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
@@ -915,8 +914,7 @@
         if (!com.android.systemui.Flags.bouncerUiRevamp()) return;
 
         if (isBouncerShowing && isExpanded()) {
-            float shadeBlurEffect = BlurConfig.maxBlurRadiusToNotificationPanelBlurRadius(
-                    mDepthController.getMaxBlurRadiusPx());
+            float shadeBlurEffect = mDepthController.getMaxBlurRadiusPx();
             mView.setRenderEffect(RenderEffect.createBlurEffect(
                     shadeBlurEffect,
                     shadeBlurEffect,
@@ -2253,7 +2251,8 @@
     }
 
     private boolean isPanelVisibleBecauseOfHeadsUp() {
-        boolean headsUpVisible = mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway;
+        boolean headsUpVisible = (mHeadsUpManager != null && mHeadsUpManager.hasPinnedHeadsUp())
+                || mHeadsUpAnimatingAway;
         return headsUpVisible && mBarState == StatusBarState.SHADE;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index 48bbb04..48e3747 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -17,6 +17,7 @@
 package com.android.systemui.shade;
 
 import static android.os.Trace.TRACE_TAG_APP;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
 
 import static com.android.systemui.Flags.enableViewCaptureTracing;
 import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -49,10 +50,12 @@
 import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.WindowInsetsController;
+import android.view.accessibility.AccessibilityEvent;
 
 import com.android.app.viewcapture.ViewCaptureFactory;
 import com.android.internal.view.FloatingActionMode;
 import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
+import com.android.systemui.Flags;
 import com.android.systemui.scene.ui.view.WindowRootView;
 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
 import com.android.systemui.statusbar.phone.ConfigurationForwarder;
@@ -77,6 +80,8 @@
 
     private SafeCloseable mViewCaptureCloseable;
 
+    private boolean mAnimatingContentLaunch = false;
+
     public NotificationShadeWindowView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setMotionEventSplittingEnabled(false);
@@ -188,6 +193,22 @@
         }
     }
 
+    @Override
+    public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+        if (Flags.shadeLaunchAccessibility() && mAnimatingContentLaunch
+                && event.getEventType() == TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
+            // Block accessibility focus events during launch animations to avoid stray TalkBack
+            // announcements.
+            return false;
+        }
+
+        return super.requestSendAccessibilityEvent(child, event);
+    }
+
+    public void setAnimatingContentLaunch(boolean animating) {
+        mAnimatingContentLaunch = animating;
+    }
+
     public void setConfigurationForwarder(ConfigurationForwarder configurationForwarder) {
         ShadeWindowGoesAround.isUnexpectedlyInLegacyMode();
         mConfigurationForwarder = configurationForwarder;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index e5dcd23..10a9fd2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -16,10 +16,12 @@
 
 package com.android.systemui.shade;
 
+import static com.android.systemui.Flags.shadeLaunchAccessibility;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
 
 import android.app.StatusBarManager;
 import android.util.Log;
@@ -43,6 +45,7 @@
 import com.android.systemui.bouncer.ui.binder.BouncerViewBinder;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlagsClassic;
@@ -59,6 +62,7 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
 import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
 import com.android.systemui.statusbar.BlurUtils;
@@ -84,7 +88,9 @@
 import com.android.systemui.window.ui.WindowRootViewBinder;
 import com.android.systemui.window.ui.viewmodel.WindowRootViewModel;
 
+import kotlinx.coroutines.CoroutineDispatcher;
 import kotlinx.coroutines.ExperimentalCoroutinesApi;
+import kotlinx.coroutines.flow.Flow;
 
 import java.io.PrintWriter;
 import java.util.Optional;
@@ -116,6 +122,7 @@
     private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
     private final QuickSettingsController mQuickSettingsController;
+    private final CoroutineDispatcher mMainDispatcher;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     private final GlanceableHubContainerController
             mGlanceableHubContainerController;
@@ -161,7 +168,6 @@
             };
     private final SystemClock mClock;
 
-    @ExperimentalCoroutinesApi
     @Inject
     public NotificationShadeWindowViewController(
             BlurUtils blurUtils,
@@ -174,6 +180,7 @@
             NotificationShadeDepthController depthController,
             NotificationShadeWindowView notificationShadeWindowView,
             ShadeViewController shadeViewController,
+            ShadeAnimationInteractor shadeAnimationInteractor,
             PanelExpansionInteractor panelExpansionInteractor,
             ShadeExpansionStateManager shadeExpansionStateManager,
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
@@ -201,7 +208,8 @@
             AlternateBouncerInteractor alternateBouncerInteractor,
             BouncerViewBinder bouncerViewBinder,
             @ShadeDisplayAware Provider<ConfigurationForwarder> configurationForwarder,
-            BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor) {
+            BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor,
+            @Main CoroutineDispatcher mainDispatcher) {
         mLockscreenShadeTransitionController = transitionController;
         mFalsingCollector = falsingCollector;
         mStatusBarStateController = statusBarStateController;
@@ -229,6 +237,7 @@
         mPrimaryBouncerInteractor = primaryBouncerInteractor;
         mAlternateBouncerInteractor = alternateBouncerInteractor;
         mQuickSettingsController = quickSettingsController;
+        mMainDispatcher = mainDispatcher;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -238,9 +247,17 @@
         collectFlow(mView, keyguardTransitionInteractor.transition(
                 Edge.create(LOCKSCREEN, DREAMING)),
                 mLockscreenToDreamingTransition);
+        Flow<Boolean> isLaunchAnimationRunning =
+                shadeLaunchAccessibility()
+                        ? combineFlows(
+                                notificationLaunchAnimationInteractor.isLaunchAnimationRunning(),
+                                shadeAnimationInteractor.isLaunchingActivity(),
+                                (notificationLaunching, shadeLaunching) ->
+                                        notificationLaunching || shadeLaunching)
+                        : notificationLaunchAnimationInteractor.isLaunchAnimationRunning();
         collectFlow(
                 mView,
-                notificationLaunchAnimationInteractor.isLaunchAnimationRunning(),
+                isLaunchAnimationRunning,
                 this::setExpandAnimationRunning);
         if (QSComposeFragment.isEnabled()) {
             collectFlow(mView,
@@ -275,7 +292,7 @@
         if (SceneContainerFlag.isEnabled()) return;
 
         WindowRootViewBinder.INSTANCE.bind(mView, windowRootViewModelFactory, blurUtils,
-                choreographer);
+                choreographer, mMainDispatcher);
     }
 
     private void bindBouncer(BouncerViewBinder bouncerViewBinder) {
@@ -726,9 +743,17 @@
             if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
                 Log.d(TAG, "Setting mExpandAnimationRunning=" + running);
             }
+
             if (running) {
                 mLaunchAnimationTimeout = mClock.uptimeMillis() + 5000;
             }
+
+            if (shadeLaunchAccessibility()) {
+                // The view needs to know when an animation is ongoing so it can intercept
+                // unnecessary accessibility events.
+                mView.setAnimatingContentLaunch(running);
+            }
+
             mExpandAnimationRunning = running;
             mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 7299f09..cf310dd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -34,8 +34,8 @@
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.plugins.qs.QSContainerController
-import com.android.systemui.recents.OverviewProxyService
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shared.system.QuickStepContract
@@ -57,7 +57,7 @@
 constructor(
     view: NotificationsQuickSettingsContainer,
     private val navigationModeController: NavigationModeController,
-    private val overviewProxyService: OverviewProxyService,
+    private val launcherProxyService: LauncherProxyService,
     private val shadeHeaderController: ShadeHeaderController,
     private val shadeInteractor: ShadeInteractor,
     private val fragmentService: FragmentService,
@@ -85,8 +85,8 @@
 
     private var isGestureNavigation = true
     private var taskbarVisible = false
-    private val taskbarVisibilityListener: OverviewProxyListener =
-        object : OverviewProxyListener {
+    private val taskbarVisibilityListener: LauncherProxyListener =
+        object : LauncherProxyListener {
             override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
                 taskbarVisible = visible
             }
@@ -134,7 +134,7 @@
 
     public override fun onViewAttached() {
         updateResources()
-        overviewProxyService.addCallback(taskbarVisibilityListener)
+        launcherProxyService.addCallback(taskbarVisibilityListener)
         mView.setInsetsChangedListener(delayedInsetSetter)
         mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
         mView.setConfigurationChangedListener { updateResources() }
@@ -142,7 +142,7 @@
     }
 
     override fun onViewDetached() {
-        overviewProxyService.removeCallback(taskbarVisibilityListener)
+        launcherProxyService.removeCallback(taskbarVisibilityListener)
         mView.removeOnInsetsChangedListener()
         mView.removeQSFragmentAttachedListener()
         mView.setConfigurationChangedListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
index 7a70966..b15615a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
@@ -5,6 +5,7 @@
 import com.android.systemui.battery.BatteryMeterView
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 import javax.inject.Inject
 
 /**
@@ -15,11 +16,9 @@
 @Inject
 constructor(
     @ShadeDisplayAware private val context: Context,
-    insetsProviderStore: StatusBarContentInsetsProviderStore,
+    private val insetsProviderStore: StatusBarContentInsetsProviderStore,
 ) {
 
-    private val insetsProvider = insetsProviderStore.defaultDisplay
-
     private companion object {
         // MotionLayout frames are in [0, 100]. Where 0 and 100 are reserved for start and end
         // frames.
@@ -43,17 +42,19 @@
      * animation.
      */
     @BatteryMeterView.BatteryPercentMode
-    fun getBatteryMode(cutout: DisplayCutout?, qsExpandedFraction: Float): Int? =
-        when {
+    fun getBatteryMode(cutout: DisplayCutout?, qsExpandedFraction: Float): Int? {
+        val insetsProvider = insetsProviderStore.forDisplay(context.displayId)
+        return when {
             qsExpandedFraction > fadeInStartFraction -> BatteryMeterView.MODE_ESTIMATE
-            qsExpandedFraction < fadeOutCompleteFraction ->
-                if (hasCenterCutout(cutout)) {
+            insetsProvider != null && qsExpandedFraction < fadeOutCompleteFraction ->
+                if (hasCenterCutout(cutout, insetsProvider)) {
                     BatteryMeterView.MODE_ON
                 } else {
                     BatteryMeterView.MODE_ESTIMATE
                 }
             else -> null
         }
+    }
 
     fun updateResources() {
         fadeInStartFraction =
@@ -64,7 +65,10 @@
                 MOTION_LAYOUT_MAX_FRAME.toFloat()
     }
 
-    private fun hasCenterCutout(cutout: DisplayCutout?): Boolean =
+    private fun hasCenterCutout(
+        cutout: DisplayCutout?,
+        insetsProvider: StatusBarContentInsetsProvider,
+    ): Boolean =
         cutout?.let {
             !insetsProvider.currentRotationHasCornerCutout() && !it.boundingRectTop.isEmpty
         } ?: false
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 5b06ad2..e6834ad 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -37,7 +37,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.withContext
@@ -47,7 +46,6 @@
  *
  * TODO(b/300258424) rename to ShadeControllerImpl and inline/delete all the deprecated methods
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class ShadeControllerSceneImpl
 @Inject
@@ -194,7 +192,6 @@
         }
     }
 
-    @ExperimentalCoroutinesApi
     override fun collapseShadeForActivityStart() {
         if (shadeInteractor.isAnyExpanded.value) {
             animateCollapseShadeForcedDelayed()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index e8a792c..9a79e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -21,6 +21,7 @@
 import android.annotation.IdRes
 import android.app.PendingIntent
 import android.app.StatusBarManager
+import android.content.Context
 import android.content.Intent
 import android.content.res.Configuration
 import android.graphics.Insets
@@ -57,6 +58,8 @@
 import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER
 import com.android.systemui.shade.carrier.ShadeCarrierGroup
 import com.android.systemui.shade.carrier.ShadeCarrierGroupController
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
 import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.phone.StatusIconContainer
@@ -69,6 +72,7 @@
 import com.android.systemui.statusbar.policy.VariableDateView
 import com.android.systemui.statusbar.policy.VariableDateViewController
 import com.android.systemui.util.ViewController
+import dagger.Lazy
 import java.io.PrintWriter
 import javax.inject.Inject
 import javax.inject.Named
@@ -90,8 +94,10 @@
     private val statusBarIconController: StatusBarIconController,
     private val tintedIconManagerFactory: TintedIconManager.Factory,
     private val privacyIconsController: HeaderPrivacyIconsController,
-    private val insetsProviderStore: StatusBarContentInsetsProviderStore,
+    private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
     @ShadeDisplayAware private val configurationController: ConfigurationController,
+    @ShadeDisplayAware private val context: Context,
+    private val shadeDisplaysRepositoryLazy: Lazy<ShadeDisplaysRepository>,
     private val variableDateViewControllerFactory: VariableDateViewController.Factory,
     @Named(SHADE_HEADER) private val batteryMeterViewController: BatteryMeterViewController,
     private val dumpManager: DumpManager,
@@ -104,7 +110,17 @@
     private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
 ) : ViewController<View>(header), Dumpable {
 
-    private val insetsProvider = insetsProviderStore.defaultDisplay
+    private val statusBarContentInsetsProvider
+        get() =
+            statusBarContentInsetsProviderStore.forDisplay(
+                if (ShadeWindowGoesAround.isEnabled) {
+                    // ShadeDisplaysRepository is the source of truth for display id when
+                    // ShadeWindowGoesAround.isEnabled
+                    shadeDisplaysRepositoryLazy.get().displayId.value
+                } else {
+                    context.displayId
+                }
+            )
 
     companion object {
         /** IDs for transitions and constraints for the [MotionLayout]. */
@@ -222,10 +238,14 @@
 
     private val insetListener =
         View.OnApplyWindowInsetsListener { view, insets ->
-            updateConstraintsForInsets(view as MotionLayout, insets)
-            lastInsets = WindowInsets(insets)
-
-            view.onApplyWindowInsets(insets)
+            val windowInsets = WindowInsets(insets)
+            if (windowInsets != lastInsets) {
+                updateConstraintsForInsets(view as MotionLayout, insets)
+                lastInsets = windowInsets
+                view.onApplyWindowInsets(insets)
+            } else {
+                insets
+            }
         }
 
     private var singleCarrier = false
@@ -285,7 +305,7 @@
             override fun onDensityOrFontScaleChanged() {
                 clock.setTextAppearance(R.style.TextAppearance_QS_Status)
                 date.setTextAppearance(R.style.TextAppearance_QS_Status)
-                mShadeCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
+                mShadeCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status)
                 loadConstraints()
                 header.minHeight =
                     resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height)
@@ -414,6 +434,7 @@
     }
 
     private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) {
+        val insetsProvider = statusBarContentInsetsProvider ?: return
         val cutout = insets.displayCutout.also { this.cutout = it }
 
         val sbInsets: Insets = insetsProvider.getStatusBarContentInsetsForCurrentRotation()
@@ -508,6 +529,9 @@
             systemIconsHoverContainer.setOnClickListener(null)
             systemIconsHoverContainer.isClickable = false
         }
+
+        lastInsets?.let { updateConstraintsForInsets(header, it) }
+
         header.jumpToState(header.startState)
         updatePosition()
         updateScrollY()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 3449e81..55ee27d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.shade
 
 import android.annotation.SuppressLint
@@ -62,7 +60,6 @@
 import dagger.Provides
 import javax.inject.Named
 import javax.inject.Provider
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Module for providing views related to the shade. */
 @Module
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
index f959f7f..4eb7072 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -48,7 +47,6 @@
 
 /** Keeps the policy and propagates the display id for the shade from it. */
 @SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
 class ShadeDisplaysRepositoryImpl
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/FocusShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/FocusShadeDisplayPolicy.kt
new file mode 100644
index 0000000..7d8f7c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/FocusShadeDisplayPolicy.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 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.shade.display
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.data.repository.FocusedDisplayRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+/** Policy that just emits the [FocusedDisplayRepository] display id. */
+@SysUISingleton
+class FocusShadeDisplayPolicy
+@Inject
+constructor(private val focusedDisplayRepository: FocusedDisplayRepository) : ShadeDisplayPolicy {
+    override val name: String
+        get() = "focused_display"
+
+    override val displayId: StateFlow<Int>
+        get() = focusedDisplayRepository.focusedDisplayId
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
index bf5deff..677e41a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
@@ -19,7 +19,8 @@
 import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor.ShadeElement
 import dagger.Binds
 import dagger.Module
-import dagger.multibindings.IntoSet
+import dagger.Provides
+import dagger.multibindings.ElementsIntoSet
 import kotlinx.coroutines.flow.StateFlow
 
 /** Describes the display the shade should be shown in. */
@@ -53,27 +54,25 @@
     fun consumeExpansionIntent(): ShadeElement?
 }
 
-@Module
+@Module(includes = [AllShadeDisplayPoliciesModule::class])
 interface ShadeDisplayPolicyModule {
 
     @Binds fun provideDefaultPolicy(impl: DefaultDisplayShadePolicy): ShadeDisplayPolicy
 
     @Binds
     fun provideShadeExpansionIntent(impl: StatusBarTouchShadeDisplayPolicy): ShadeExpansionIntent
+}
 
-    @IntoSet
-    @Binds
-    fun provideDefaultDisplayPolicyToSet(impl: DefaultDisplayShadePolicy): ShadeDisplayPolicy
-
-    @IntoSet
-    @Binds
-    fun provideAnyExternalShadeDisplayPolicyToSet(
-        impl: AnyExternalShadeDisplayPolicy
-    ): ShadeDisplayPolicy
-
-    @Binds
-    @IntoSet
-    fun provideStatusBarTouchShadeDisplayPolicy(
-        impl: StatusBarTouchShadeDisplayPolicy
-    ): ShadeDisplayPolicy
+@Module
+internal object AllShadeDisplayPoliciesModule {
+    @Provides
+    @ElementsIntoSet
+    fun provideShadeDisplayPolicies(
+        defaultPolicy: DefaultDisplayShadePolicy,
+        externalPolicy: AnyExternalShadeDisplayPolicy,
+        statusBarPolicy: StatusBarTouchShadeDisplayPolicy,
+        focusPolicy: FocusShadeDisplayPolicy,
+    ): Set<ShadeDisplayPolicy> {
+        return setOf(defaultPolicy, externalPolicy, statusBarPolicy, focusPolicy)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
index dfdf2ad..3520948 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.shade.domain.interactor
 
 import com.android.systemui.dagger.SysUISingleton
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
index cea521f..c447a19 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.shade.domain.interactor
 
 import com.android.compose.animation.scene.ContentKey
@@ -26,7 +24,6 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
index 8467185..615c2fd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.shade.data.repository.ShadeAnimationRepository
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
@@ -36,7 +35,6 @@
 @SysUISingleton
 class ShadeAnimationInteractorSceneContainerImpl
 @Inject
-@OptIn(ExperimentalCoroutinesApi::class)
 constructor(
     @Background scope: CoroutineScope,
     shadeAnimationRepository: ShadeAnimationRepository,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
index 884cfc10..5a63034 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
@@ -22,10 +22,8 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Implementation of ShadeBackActionInteractor backed by scenes. */
-@OptIn(ExperimentalCoroutinesApi::class)
 class ShadeBackActionInteractorImpl
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 992385c..661f2ae 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -35,7 +35,6 @@
 import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -46,7 +45,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** ShadeInteractor implementation for Scene Container. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class ShadeInteractorSceneContainerImpl
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
index edf503d..0145150 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
@@ -16,17 +16,23 @@
 
 package com.android.systemui.shade.domain.interactor
 
+import android.provider.Settings
 import androidx.annotation.FloatRange
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -76,29 +82,54 @@
 
 class ShadeModeInteractorImpl
 @Inject
-constructor(@Application applicationScope: CoroutineScope, repository: ShadeRepository) :
-    ShadeModeInteractor {
+constructor(
+    @Application applicationScope: CoroutineScope,
+    private val repository: ShadeRepository,
+    secureSettingsRepository: SecureSettingsRepository,
+    @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
+) : ShadeModeInteractor {
+
+    private val isDualShadeEnabled: Flow<Boolean> =
+        secureSettingsRepository.boolSetting(
+            Settings.Secure.DUAL_SHADE,
+            defaultValue = DUAL_SHADE_ENABLED_DEFAULT,
+        )
 
     override val isShadeLayoutWide: StateFlow<Boolean> = repository.isShadeLayoutWide
 
-    override val shadeMode: StateFlow<ShadeMode> =
-        isShadeLayoutWide
-            .map(this::determineShadeMode)
-            .stateIn(
-                applicationScope,
-                SharingStarted.Eagerly,
-                initialValue = determineShadeMode(isShadeLayoutWide.value),
+    private val shadeModeInitialValue: ShadeMode
+        get() =
+            determineShadeMode(
+                isDualShadeEnabled = DUAL_SHADE_ENABLED_DEFAULT,
+                isShadeLayoutWide = repository.isShadeLayoutWide.value,
             )
 
+    override val shadeMode: StateFlow<ShadeMode> =
+        combine(isDualShadeEnabled, repository.isShadeLayoutWide, ::determineShadeMode)
+            .logDiffsForTable(tableLogBuffer = tableLogBuffer, initialValue = shadeModeInitialValue)
+            .stateIn(applicationScope, SharingStarted.Eagerly, initialValue = shadeModeInitialValue)
+
     @FloatRange(from = 0.0, to = 1.0) override fun getTopEdgeSplitFraction(): Float = 0.5f
 
-    private fun determineShadeMode(isShadeLayoutWide: Boolean): ShadeMode {
+    private fun determineShadeMode(
+        isDualShadeEnabled: Boolean,
+        isShadeLayoutWide: Boolean,
+    ): ShadeMode {
         return when {
-            DualShade.isEnabled -> ShadeMode.Dual
+            isDualShadeEnabled ||
+                // TODO(b/388793191): This ensures that the dual_shade aconfig flag can also enable
+                //  Dual Shade, to avoid breaking unit tests. Remove this once all references to the
+                //  flag are removed.
+                DualShade.isEnabled -> ShadeMode.Dual
             isShadeLayoutWide -> ShadeMode.Split
             else -> ShadeMode.Single
         }
     }
+
+    companion object {
+        /* Whether the Dual Shade setting is enabled by default. */
+        private const val DUAL_SHADE_ENABLED_DEFAULT = false
+    }
 }
 
 class ShadeModeInteractorEmptyImpl @Inject constructor() : ShadeModeInteractor {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt
index a8199a4..8b3ce0f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt
@@ -16,15 +16,18 @@
 
 package com.android.systemui.shade.shared.model
 
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
 /** Enumerates all known modes of operation of the shade. */
-sealed interface ShadeMode {
+sealed class ShadeMode : Diffable<ShadeMode> {
 
     /**
      * The single or "accordion" shade where the QS and notification parts are in two vertically
      * stacked panels and the user can swipe up and down to expand or collapse between the two
      * parts.
      */
-    data object Single : ShadeMode
+    data object Single : ShadeMode()
 
     /**
      * The split shade where, on large screens and unfolded foldables, the QS and notification parts
@@ -32,14 +35,18 @@
      *
      * Note: This isn't the only mode where the shade is wide.
      */
-    data object Split : ShadeMode
+    data object Split : ShadeMode()
 
     /**
      * The dual shade where the QS and notification parts each have their own independently
      * expandable/collapsible panel on either side of the large screen / unfolded device or sharing
      * a space on a small screen or folded device.
      */
-    data object Dual : ShadeMode
+    data object Dual : ShadeMode()
+
+    override fun logDiffs(prevVal: ShadeMode, row: TableRowLogger) {
+        row.logChange("shadeMode", toString())
+    }
 
     companion object {
         @JvmStatic fun dual(): Dual = Dual
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
index 02531221..7fd0e4e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.shade.ui.viewmodel
 
 import androidx.lifecycle.LifecycleOwner
@@ -35,7 +33,6 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import java.util.concurrent.atomic.AtomicBoolean
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 7fc1510..1720898 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -111,7 +111,7 @@
     private static final int MSG_COLLAPSE_PANELS                   = 4 << MSG_SHIFT;
     private static final int MSG_EXPAND_SETTINGS                   = 5 << MSG_SHIFT;
     private static final int MSG_SYSTEM_BAR_CHANGED                = 6 << MSG_SHIFT;
-    private static final int MSG_DISPLAY_READY                     = 7 << MSG_SHIFT;
+    private static final int MSG_DISPLAY_ADD_SYSTEM_DECORATIONS    = 7 << MSG_SHIFT;
     private static final int MSG_SHOW_IME_BUTTON                   = 8 << MSG_SHIFT;
     private static final int MSG_TOGGLE_RECENT_APPS                = 9 << MSG_SHIFT;
     private static final int MSG_PRELOAD_RECENT_APPS               = 10 << MSG_SHIFT;
@@ -273,12 +273,12 @@
         default void toggleQuickSettingsPanel() { }
 
         /**
-         * Called to notify IME window status changes.
+         * Sets the new IME window status.
          *
-         * @param displayId The id of the display to notify.
-         * @param vis IME visibility.
-         * @param backDisposition Disposition mode of back button.
-         * @param showImeSwitcher {@code true} to show IME switch button.
+         * @param displayId The id of the display to which the IME is bound.
+         * @param vis The IME window visibility.
+         * @param backDisposition The IME back disposition mode.
+         * @param showImeSwitcher Whether the IME Switcher button should be shown.
          */
         default void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
                 @BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
@@ -415,9 +415,9 @@
         }
 
         /**
-         * @see IStatusBar#onDisplayReady(int)
+         * @see IStatusBar#onDisplayAddSystemDecorations(int)
          */
-        default void onDisplayReady(int displayId) {
+        default void onDisplayAddSystemDecorations(int displayId) {
         }
 
         /**
@@ -1205,9 +1205,9 @@
     }
 
     @Override
-    public void onDisplayReady(int displayId) {
+    public void onDisplayAddSystemDecorations(int displayId) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_DISPLAY_READY, displayId, 0).sendToTarget();
+            mHandler.obtainMessage(MSG_DISPLAY_ADD_SYSTEM_DECORATIONS, displayId, 0).sendToTarget();
         }
     }
 
@@ -1851,9 +1851,9 @@
                         mCallbacks.get(i).showPinningEscapeToast();
                     }
                     break;
-                case MSG_DISPLAY_READY:
+                case MSG_DISPLAY_ADD_SYSTEM_DECORATIONS:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onDisplayReady(msg.arg1);
+                        mCallbacks.get(i).onDisplayAddSystemDecorations(msg.arg1);
                     }
                     break;
                 case MSG_DISPLAY_REMOVE_SYSTEM_DECORATIONS:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 2544323..79a872e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -573,13 +573,13 @@
                                 Pair.create(KeyEvent.KEYCODE_ESCAPE, KeyEvent.META_META_ON),
                                 Pair.create(KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON),
                                 Pair.create(KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.META_META_ON))),
-                /* Take a full screenshot: Meta + Ctrl + S */
+                /* Take a full screenshot: Meta + S */
                 new ShortcutKeyGroupMultiMappingInfo(
                         context.getString(R.string.group_system_full_screenshot),
                         Arrays.asList(
                                 Pair.create(
                                         KeyEvent.KEYCODE_S,
-                                        KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON))),
+                                        KeyEvent.META_META_ON))),
                 /* Access list of system / apps shortcuts: Meta + / */
                 new ShortcutKeyGroupMultiMappingInfo(
                         context.getString(R.string.group_system_access_system_app_shortcuts),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index a5595ed..4269f60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -88,6 +88,7 @@
 import com.android.keyguard.logging.KeyguardLogger;
 import com.android.settingslib.Utils;
 import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.Flags;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FaceHelpMessageDeferral;
 import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory;
@@ -199,7 +200,7 @@
     private CharSequence mBiometricMessage;
     private CharSequence mBiometricMessageFollowUp;
     private BiometricSourceType mBiometricMessageSource;
-    protected ColorStateList mInitialTextColorState;
+    private ColorStateList mInitialTextColorState;
     private boolean mVisible;
     private boolean mOrganizationOwnedDevice;
 
@@ -393,13 +394,27 @@
         return mIndicationArea;
     }
 
+    /**
+     * Notify controller about configuration changes.
+     */
+    public void onConfigurationChanged() {
+        // Get new text color in case theme has changed
+        if (Flags.indicationTextA11yFix()) {
+            setIndicationColorToThemeColor();
+        }
+    }
+
     public void setIndicationArea(ViewGroup indicationArea) {
         mIndicationArea = indicationArea;
         mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text);
         mLockScreenIndicationView = indicationArea.findViewById(
                 R.id.keyguard_indication_text_bottom);
-        mInitialTextColorState = mTopIndicationView != null
-                ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
+        if (Flags.indicationTextA11yFix()) {
+            setIndicationColorToThemeColor();
+        } else {
+            setIndicationTextColor(mTopIndicationView != null
+                    ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE));
+        }
         if (mRotateTextViewController != null) {
             mRotateTextViewController.destroy();
         }
@@ -436,6 +451,12 @@
                 mIsLogoutEnabledCallback);
     }
 
+    @NonNull
+    private ColorStateList wallpaperTextColor() {
+        return ColorStateList.valueOf(
+                Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor));
+    }
+
     /**
      * Cleanup
      */
@@ -513,7 +534,7 @@
                             .setMessage(mContext.getResources().getString(
                                     com.android.systemui.res.R.string.dismissible_keyguard_swipe)
                             )
-                            .setTextColor(mInitialTextColorState)
+                            .setTextColor(getInitialTextColorState())
                             .build(),
                     /* updateImmediately */ true);
         } else {
@@ -533,7 +554,7 @@
                               INDICATION_TYPE_DISCLOSURE,
                               new KeyguardIndication.Builder()
                                       .setMessage(disclosure)
-                                      .setTextColor(mInitialTextColorState)
+                                      .setTextColor(getInitialTextColorState())
                                       .build(),
                               /* updateImmediately */ false);
                     }
@@ -602,7 +623,7 @@
                             INDICATION_TYPE_OWNER_INFO,
                             new KeyguardIndication.Builder()
                                     .setMessage(finalInfo)
-                                    .setTextColor(mInitialTextColorState)
+                                    .setTextColor(getInitialTextColorState())
                                     .build(),
                             false);
                 } else {
@@ -624,7 +645,7 @@
                     INDICATION_TYPE_BATTERY,
                     new KeyguardIndication.Builder()
                             .setMessage(powerIndication)
-                            .setTextColor(mInitialTextColorState)
+                            .setTextColor(getInitialTextColorState())
                             .build(),
                     animate);
         } else {
@@ -645,7 +666,7 @@
                     new KeyguardIndication.Builder()
                             .setMessage(mContext.getResources().getText(
                                     com.android.internal.R.string.lockscreen_storage_locked))
-                            .setTextColor(mInitialTextColorState)
+                            .setTextColor(getInitialTextColorState())
                             .build(),
                     false);
         } else {
@@ -666,7 +687,7 @@
                             .setMessage(mBiometricMessage)
                             .setForceAccessibilityLiveRegionAssertive()
                             .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
-                            .setTextColor(mInitialTextColorState)
+                            .setTextColor(getInitialTextColorState())
                             .build(),
                     true
             );
@@ -680,7 +701,7 @@
                     new KeyguardIndication.Builder()
                             .setMessage(mBiometricMessageFollowUp)
                             .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
-                            .setTextColor(mInitialTextColorState)
+                            .setTextColor(getInitialTextColorState())
                             .build(),
                     true
             );
@@ -711,7 +732,7 @@
                     INDICATION_TYPE_TRUST,
                     new KeyguardIndication.Builder()
                             .setMessage(trustGrantedIndication)
-                            .setTextColor(mInitialTextColorState)
+                            .setTextColor(getInitialTextColorState())
                             .build(),
                     true);
             hideBiometricMessage();
@@ -722,7 +743,7 @@
                     INDICATION_TYPE_TRUST,
                     new KeyguardIndication.Builder()
                             .setMessage(trustManagedIndication)
-                            .setTextColor(mInitialTextColorState)
+                            .setTextColor(getInitialTextColorState())
                             .build(),
                     false);
         } else {
@@ -751,7 +772,7 @@
                     INDICATION_TYPE_PERSISTENT_UNLOCK_MESSAGE,
                     new KeyguardIndication.Builder()
                             .setMessage(mPersistentUnlockMessage)
-                            .setTextColor(mInitialTextColorState)
+                            .setTextColor(getInitialTextColorState())
                             .build(),
                     true);
         } else {
@@ -792,7 +813,7 @@
                     new KeyguardIndication.Builder()
                             .setMessage(mContext.getString(
                                     R.string.keyguard_indication_after_adaptive_auth_lock))
-                            .setTextColor(mInitialTextColorState)
+                            .setTextColor(getInitialTextColorState())
                             .build(),
                     true);
         } else {
@@ -1179,7 +1200,8 @@
                 } else {
                     message = mContext.getString(R.string.keyguard_retry);
                 }
-                mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState,
+                mStatusBarKeyguardViewManager.setKeyguardMessage(message,
+                        getInitialTextColorState(),
                         null);
             }
         } else {
@@ -1232,7 +1254,7 @@
 
     public void dump(PrintWriter pw, String[] args) {
         pw.println("KeyguardIndicationController:");
-        pw.println("  mInitialTextColorState: " + mInitialTextColorState);
+        pw.println("  mInitialTextColorState: " + getInitialTextColorState());
         pw.println("  mPowerPluggedInWired: " + mPowerPluggedInWired);
         pw.println("  mPowerPluggedIn: " + mPowerPluggedIn);
         pw.println("  mPowerCharged: " + mPowerCharged);
@@ -1253,6 +1275,22 @@
         mRotateTextViewController.dump(pw, args);
     }
 
+    protected ColorStateList getInitialTextColorState() {
+        return mInitialTextColorState;
+    }
+
+    private void setIndicationColorToThemeColor() {
+        mInitialTextColorState = wallpaperTextColor();
+    }
+
+    /**
+     * @deprecated Use {@link #setIndicationColorToThemeColor}
+     */
+    @Deprecated
+    private void setIndicationTextColor(ColorStateList color) {
+        mInitialTextColorState = color;
+    }
+
     protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
         @Override
         public void onTimeChanged() {
@@ -1358,7 +1396,7 @@
                     mBouncerMessageInteractor.setFaceAcquisitionMessage(helpString);
                 }
                 mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
-                        mInitialTextColorState, biometricSourceType);
+                        getInitialTextColorState(), biometricSourceType);
             } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
                 if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
                     showBiometricMessage(
@@ -1655,7 +1693,7 @@
     private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg,
             BiometricSourceType biometricSourceType) {
         if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
-            mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState,
+            mStatusBarKeyguardViewManager.setKeyguardMessage(errString, getInitialTextColorState(),
                     biometricSourceType);
         } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
             showBiometricMessage(errString, followUpMsg, biometricSourceType);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index ccea254..4c6fa48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -284,7 +284,8 @@
                     /* rankingAdjustment= */ 0,
                     /* isBubble= */ false,
                     /* proposedImportance= */ 0,
-                    /* sensitiveContent= */ false
+                    /* sensitiveContent= */ false,
+                    /* summarization = */ null
             );
         }
         return ranking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index c8f9727..382fc70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -70,7 +70,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -152,7 +152,7 @@
     private final List<UserChangedListener> mListeners = new ArrayList<>();
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final NotificationClickNotifier mClickNotifier;
-    private final Lazy<OverviewProxyService> mOverviewProxyServiceLazy;
+    private final Lazy<LauncherProxyService> mLauncherProxyServiceLazy;
     private final FeatureFlagsClassic mFeatureFlags;
     private boolean mShowLockscreenNotifications;
     private LockPatternUtils mLockPatternUtils;
@@ -235,8 +235,8 @@
                 if (!keyguardPrivateNotifications()) {
                     // Start the overview connection to the launcher service
                     // Connect if user hasn't connected yet
-                    if (mOverviewProxyServiceLazy.get().getProxy() == null) {
-                        mOverviewProxyServiceLazy.get().startConnectionToCurrentUser();
+                    if (mLauncherProxyServiceLazy.get().getProxy() == null) {
+                        mLauncherProxyServiceLazy.get().startConnectionToCurrentUser();
                     }
                 }
             } else if (Objects.equals(action, NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION)) {
@@ -318,7 +318,7 @@
             Lazy<NotificationVisibilityProvider> visibilityProviderLazy,
             Lazy<CommonNotifCollection> commonNotifCollectionLazy,
             NotificationClickNotifier clickNotifier,
-            Lazy<OverviewProxyService> overviewProxyServiceLazy,
+            Lazy<LauncherProxyService> launcherProxyServiceLazy,
             KeyguardManager keyguardManager,
             StatusBarStateController statusBarStateController,
             @Main Executor mainExecutor,
@@ -343,7 +343,7 @@
         mVisibilityProviderLazy = visibilityProviderLazy;
         mCommonNotifCollectionLazy = commonNotifCollectionLazy;
         mClickNotifier = clickNotifier;
-        mOverviewProxyServiceLazy = overviewProxyServiceLazy;
+        mLauncherProxyServiceLazy = launcherProxyServiceLazy;
         statusBarStateController.addCallback(this);
         mLockPatternUtils = lockPatternUtils;
         mKeyguardManager = keyguardManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index ca2fbdd..a2a8409 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -54,7 +54,6 @@
 import com.android.systemui.util.WallpaperController
 import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
 import com.android.wm.shell.appzoomout.AppZoomOut
-
 import java.io.PrintWriter
 import java.util.Optional
 import javax.inject.Inject
@@ -280,9 +279,7 @@
         wallpaperController.setNotificationShadeZoom(zoomOutFromShadeRadius)
         if (spatialModelAppPushback()) {
             appZoomOutOptional.ifPresent { appZoomOut ->
-                appZoomOut.setProgress(
-                    zoomOutFromShadeRadius
-                )
+                appZoomOut.setProgress(zoomOutFromShadeRadius)
             }
         }
         listeners.forEach {
@@ -526,7 +523,7 @@
     private fun scheduleUpdate() {
         val (blur, zoomOutFromShadeRadius) = computeBlurAndZoomOut()
         zoomOutCalculatedFromShadeRadius = zoomOutFromShadeRadius
-        if (Flags.bouncerUiRevamp()) {
+        if (Flags.bouncerUiRevamp() || Flags.glanceableHubBlurredBackground()) {
             updateScheduled =
                 windowRootViewBlurInteractor.requestBlurForShade(blur, shouldBlurBeOpaque)
             return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
index 49c4479..49d69f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
@@ -53,6 +54,9 @@
     private val packageManager: PackageManager,
     @StatusBarChipsLog private val logger: LogBuffer,
 ) {
+    val projectionStartedDuringCallAndActivePostCallEvent: Flow<Unit> =
+        mediaProjectionRepository.projectionStartedDuringCallAndActivePostCallEvent
+
     val projection: StateFlow<ProjectionChipModel> =
         mediaProjectionRepository.mediaProjectionState
             .map { state ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/model/MediaProjectionStopDialogModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/model/MediaProjectionStopDialogModel.kt
new file mode 100644
index 0000000..b37c762
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/model/MediaProjectionStopDialogModel.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2025 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.chips.mediaprojection.domain.model
+
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/** Represents the visibility state of a media projection stop dialog. */
+sealed interface MediaProjectionStopDialogModel {
+    /** The dialog is hidden and not visible to the user. */
+    data object Hidden : MediaProjectionStopDialogModel
+
+    /** The dialog is shown to the user. */
+    data class Shown(
+        val dialogDelegate: SystemUIDialog.Delegate,
+        private val onDismissAction: () -> Unit,
+    ) : MediaProjectionStopDialogModel {
+        /**
+         * Creates and shows the dialog. Ensures that onDismissAction callback is invoked when the
+         * dialog is canceled or dismissed.
+         */
+        fun createAndShowDialog() {
+            val dialog = dialogDelegate.createDialog()
+            dialog.setOnCancelListener { onDismissAction.invoke() }
+            dialog.setOnDismissListener { onDismissAction.invoke() }
+            dialog.show()
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
index 836cf49..cece521 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
@@ -27,7 +27,6 @@
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.combine
@@ -45,7 +44,6 @@
  *
  * @property creationTime the time when the notification first appeared as promoted.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 class SingleNotificationChipInteractor
 @AssistedInject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
index 4fad01d..9463db5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -46,7 +45,6 @@
 
 /** An interactor for the notification chips shown in the status bar. */
 @SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
 class StatusBarNotificationChipsInteractor
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index b7cad62..46456b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.statusbar.notification.domain.model.TopPinnedState
 import com.android.systemui.statusbar.notification.headsup.PinnedStatus
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -73,17 +74,24 @@
                 OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(this.key)
             }
         val colors = this.promotedContent.toCustomColorsModel()
-        val onClickListener =
-            View.OnClickListener {
-                // The notification pipeline needs everything to run on the main thread, so keep
-                // this event on the main thread.
-                applicationScope.launch {
-                    notifChipsInteractor.onPromotedNotificationChipTapped(
-                        this@toActivityChipModel.key
-                    )
-                }
+
+        val clickListener: () -> Unit = {
+            // The notification pipeline needs everything to run on the main thread, so keep
+            // this event on the main thread.
+            applicationScope.launch {
+                notifChipsInteractor.onPromotedNotificationChipTapped(this@toActivityChipModel.key)
             }
-        val clickBehavior = OngoingActivityChipModel.ClickBehavior.None
+        }
+        val onClickListenerLegacy =
+            View.OnClickListener {
+                StatusBarChipsModernization.assertInLegacyMode()
+                clickListener.invoke()
+            }
+        val clickBehavior =
+            OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification({
+                StatusBarChipsModernization.assertInNewMode()
+                clickListener.invoke()
+            })
 
         val isShowingHeadsUpFromChipTap =
             headsUpState is TopPinnedState.Pinned &&
@@ -95,7 +103,7 @@
             return OngoingActivityChipModel.Shown.IconOnly(
                 icon,
                 colors,
-                onClickListener,
+                onClickListenerLegacy,
                 clickBehavior,
             )
         }
@@ -105,7 +113,7 @@
                 icon,
                 colors,
                 this.promotedContent.shortCriticalText,
-                onClickListener,
+                onClickListenerLegacy,
                 clickBehavior,
             )
         }
@@ -121,7 +129,7 @@
             return OngoingActivityChipModel.Shown.IconOnly(
                 icon,
                 colors,
-                onClickListener,
+                onClickListenerLegacy,
                 clickBehavior,
             )
         }
@@ -130,7 +138,7 @@
             return OngoingActivityChipModel.Shown.IconOnly(
                 icon,
                 colors,
-                onClickListener,
+                onClickListenerLegacy,
                 clickBehavior,
             )
         }
@@ -140,7 +148,7 @@
                     icon,
                     colors,
                     time = this.promotedContent.time.time,
-                    onClickListener,
+                    onClickListenerLegacy,
                     clickBehavior,
                 )
             }
@@ -149,7 +157,7 @@
                     icon,
                     colors,
                     startTimeMs = this.promotedContent.time.time,
-                    onClickListener,
+                    onClickListenerLegacy,
                     clickBehavior,
                 )
             }
@@ -159,7 +167,7 @@
                     icon,
                     colors,
                     startTimeMs = this.promotedContent.time.time,
-                    onClickListener,
+                    onClickListenerLegacy,
                     clickBehavior,
                 )
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index 0b5e669..572c7fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -43,7 +42,6 @@
 
 /** Interactor for the screen recording chip shown in the status bar. */
 @SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
 class ScreenRecordChipInteractor
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
index 6654d4a..7a46fff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import androidx.annotation.DrawableRes
 import com.android.internal.jank.Cuj
+import com.android.systemui.CoreStartable
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.common.shared.model.ContentDescription
@@ -31,6 +32,7 @@
 import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
 import com.android.systemui.statusbar.chips.StatusBarChipsLog
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel
 import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
 import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
 import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndGenericShareToAppDialogDelegate
@@ -41,13 +43,18 @@
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickCallback
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /**
  * View model for the share-to-app chip, shown when sharing your phone screen content to another app
@@ -64,7 +71,59 @@
     private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     @StatusBarChipsLog private val logger: LogBuffer,
-) : OngoingActivityChipViewModel {
+) : OngoingActivityChipViewModel, CoreStartable {
+
+    private val _stopDialogToShow: MutableStateFlow<MediaProjectionStopDialogModel> =
+        MutableStateFlow(MediaProjectionStopDialogModel.Hidden)
+
+    /**
+     * Represents the current state of the media projection stop dialog. Emits
+     * [MediaProjectionStopDialogModel.Shown] when the dialog should be displayed, and
+     * [MediaProjectionStopDialogModel.Hidden] when it is dismissed.
+     */
+    val stopDialogToShow: StateFlow<MediaProjectionStopDialogModel> =
+        _stopDialogToShow.asStateFlow()
+
+    /**
+     * Emits a [MediaProjectionStopDialogModel] based on the current projection state when a
+     * projectionStartedDuringCallAndActivePostCallEvent event is emitted. If projecting, determines
+     * the appropriate dialog type to show. Otherwise, emits a hidden dialog state.
+     */
+    private val stopDialogDueToCallEndedState: StateFlow<MediaProjectionStopDialogModel> =
+        mediaProjectionChipInteractor.projectionStartedDuringCallAndActivePostCallEvent
+            .sample(mediaProjectionChipInteractor.projection) { _, currentProjection ->
+                when (currentProjection) {
+                    is ProjectionChipModel.NotProjecting -> MediaProjectionStopDialogModel.Hidden
+                    is ProjectionChipModel.Projecting -> {
+                        when (currentProjection.receiver) {
+                            ProjectionChipModel.Receiver.ShareToApp -> {
+                                when (currentProjection.contentType) {
+                                    ProjectionChipModel.ContentType.Screen ->
+                                        createShareScreenToAppStopDialog(currentProjection)
+                                    ProjectionChipModel.ContentType.Audio ->
+                                        createGenericShareScreenToAppStopDialog()
+                                }
+                            }
+                            ProjectionChipModel.Receiver.CastToOtherDevice ->
+                                MediaProjectionStopDialogModel.Hidden
+                        }
+                    }
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), MediaProjectionStopDialogModel.Hidden)
+
+    /**
+     * Initializes background flow collector during SysUI startup for events determining the
+     * visibility of media projection stop dialogs.
+     */
+    override fun start() {
+        if (com.android.media.projection.flags.Flags.showStopDialogPostCallEnd()) {
+            scope.launch {
+                stopDialogDueToCallEndedState.collect { event -> _stopDialogToShow.value = event }
+            }
+        }
+    }
+
     private val internalChip =
         mediaProjectionChipInteractor.projection
             .map { projectionModel ->
@@ -92,7 +151,25 @@
     private val chipTransitionHelper = ChipTransitionHelper(scope)
 
     override val chip: StateFlow<OngoingActivityChipModel> =
-        chipTransitionHelper.createChipFlow(internalChip)
+        combine(chipTransitionHelper.createChipFlow(internalChip), stopDialogToShow) {
+                currentChip,
+                stopDialog ->
+                if (
+                    com.android.media.projection.flags.Flags.showStopDialogPostCallEnd() &&
+                        stopDialog is MediaProjectionStopDialogModel.Shown
+                ) {
+                    logger.log(
+                        TAG,
+                        LogLevel.INFO,
+                        {},
+                        { "Hiding the chip as stop dialog is being shown" },
+                    )
+                    OngoingActivityChipModel.Hidden()
+                } else {
+                    currentChip
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden())
 
     /**
      * Notifies this class that the user just stopped a screen recording from the dialog that's
@@ -108,6 +185,12 @@
         chipTransitionHelper.onActivityStoppedFromDialog()
     }
 
+    /** Called when the stop dialog is dismissed or cancelled. */
+    private fun onStopDialogDismissed() {
+        logger.log(TAG, LogLevel.INFO, {}, { "The media projection stop dialog was dismissed" })
+        _stopDialogToShow.value = MediaProjectionStopDialogModel.Hidden
+    }
+
     /** Stops the currently active projection. */
     private fun stopProjectingFromDialog() {
         logger.log(TAG, LogLevel.INFO, {}, { "Stop sharing requested from dialog" })
@@ -115,6 +198,24 @@
         mediaProjectionChipInteractor.stopProjecting()
     }
 
+    private fun createShareScreenToAppStopDialog(
+        projectionModel: ProjectionChipModel.Projecting
+    ): MediaProjectionStopDialogModel {
+        val dialogDelegate = createShareScreenToAppDialogDelegate(projectionModel)
+        return MediaProjectionStopDialogModel.Shown(
+            dialogDelegate,
+            onDismissAction = ::onStopDialogDismissed,
+        )
+    }
+
+    private fun createGenericShareScreenToAppStopDialog(): MediaProjectionStopDialogModel {
+        val dialogDelegate = createGenericShareToAppDialogDelegate()
+        return MediaProjectionStopDialogModel.Shown(
+            dialogDelegate,
+            onDismissAction = ::onStopDialogDismissed,
+        )
+    }
+
     private fun createShareScreenToAppChip(
         state: ProjectionChipModel.Projecting
     ): OngoingActivityChipModel.Shown {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index d46638f..f5764d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -418,6 +418,7 @@
             }
             is OngoingActivityChipModel.Shown.Timer,
             is OngoingActivityChipModel.Shown.Text,
+            is OngoingActivityChipModel.Shown.ShortTimeDelta,
             is OngoingActivityChipModel.Shown.IconOnly -> {
                 chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
new file mode 100644
index 0000000..375e029
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2025 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.chips.ui.compose
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.constrain
+import androidx.compose.ui.unit.dp
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.rememberChronometerState
+
+@Composable
+fun ChipContent(viewModel: OngoingActivityChipModel.Shown, modifier: Modifier = Modifier) {
+    val context = LocalContext.current
+    val isTextOnly = viewModel.icon == null
+    val hasEmbeddedIcon =
+        viewModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView ||
+            viewModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon
+    val startPadding =
+        if (isTextOnly || hasEmbeddedIcon) {
+            0.dp
+        } else {
+            dimensionResource(id = R.dimen.ongoing_activity_chip_icon_text_padding)
+        }
+    val endPadding =
+        if (hasEmbeddedIcon) {
+            dimensionResource(
+                id = R.dimen.ongoing_activity_chip_text_end_padding_for_embedded_padding_icon
+            )
+        } else {
+            0.dp
+        }
+    val textStyle = MaterialTheme.typography.labelLarge
+    val textColor = Color(viewModel.colors.text(context))
+    when (viewModel) {
+        is OngoingActivityChipModel.Shown.Timer -> {
+            val timerState = rememberChronometerState(startTimeMillis = viewModel.startTimeMs)
+            Text(
+                text = timerState.currentTimeText,
+                style = textStyle,
+                color = textColor,
+                modifier =
+                    modifier.padding(start = startPadding, end = endPadding).neverDecreaseWidth(),
+            )
+        }
+
+        is OngoingActivityChipModel.Shown.Countdown -> {
+            ChipText(
+                text = viewModel.secondsUntilStarted.toString(),
+                style = textStyle,
+                color = textColor,
+                modifier =
+                    modifier.padding(start = startPadding, end = endPadding).neverDecreaseWidth(),
+                backgroundColor = Color(viewModel.colors.background(context).defaultColor),
+            )
+        }
+
+        is OngoingActivityChipModel.Shown.Text -> {
+            ChipText(
+                text = viewModel.text,
+                style = textStyle,
+                color = textColor,
+                modifier = modifier.padding(start = startPadding, end = endPadding),
+                backgroundColor = Color(viewModel.colors.background(context).defaultColor),
+            )
+        }
+
+        is OngoingActivityChipModel.Shown.ShortTimeDelta -> {
+            // TODO(b/372657935): Implement ShortTimeDelta content in compose.
+        }
+
+        is OngoingActivityChipModel.Shown.IconOnly -> {
+            throw IllegalStateException("ChipContent should only be used if the chip shows text")
+        }
+    }
+}
+
+/** A modifier that ensures the width of the content only increases and never decreases. */
+private fun Modifier.neverDecreaseWidth(): Modifier {
+    return this.then(neverDecreaseWidthElement)
+}
+
+private data object neverDecreaseWidthElement : ModifierNodeElement<NeverDecreaseWidthNode>() {
+    override fun create(): NeverDecreaseWidthNode {
+        return NeverDecreaseWidthNode()
+    }
+
+    override fun update(node: NeverDecreaseWidthNode) {
+        error("This should never be called")
+    }
+}
+
+private class NeverDecreaseWidthNode : Modifier.Node(), LayoutModifierNode {
+    private var minWidth = 0
+
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+    ): MeasureResult {
+        val placeable = measurable.measure(Constraints(minWidth = minWidth).constrain(constraints))
+        val width = placeable.width
+        val height = placeable.height
+
+        minWidth = maxOf(minWidth, width)
+
+        return layout(width, height) { placeable.place(0, 0) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index a682f96..647f3bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -29,8 +29,6 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.widthIn
 import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -45,10 +43,8 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.chips.ui.compose.modifiers.neverDecreaseWidth
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.chips.ui.viewmodel.rememberChronometerState
 
 @Composable
 fun OngoingActivityChip(model: OngoingActivityChipModel.Shown, modifier: Modifier = Modifier) {
@@ -66,6 +62,9 @@
                 ChipBody(model, onClick = { clickBehavior.onClick(expandable) })
             }
         }
+        is OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification -> {
+            ChipBody(model, onClick = { clickBehavior.onClick() })
+        }
 
         is OngoingActivityChipModel.ClickBehavior.None -> {
             ChipBody(model, modifier = modifier)
@@ -120,32 +119,8 @@
             model.icon?.let { ChipIcon(viewModel = it, colors = model.colors) }
 
             val isIconOnly = model is OngoingActivityChipModel.Shown.IconOnly
-            val isTextOnly = model.icon == null
             if (!isIconOnly) {
-                ChipContent(
-                    viewModel = model,
-                    modifier =
-                        Modifier.padding(
-                            start =
-                                if (isTextOnly || hasEmbeddedIcon) {
-                                    0.dp
-                                } else {
-                                    dimensionResource(
-                                        id = R.dimen.ongoing_activity_chip_icon_text_padding
-                                    )
-                                },
-                            end =
-                                if (hasEmbeddedIcon) {
-                                    dimensionResource(
-                                        id =
-                                            R.dimen
-                                                .ongoing_activity_chip_text_end_padding_for_embedded_padding_icon
-                                    )
-                                } else {
-                                    0.dp
-                                },
-                        ),
-                )
+                ChipContent(viewModel = model)
             }
         }
     }
@@ -193,46 +168,6 @@
 }
 
 @Composable
-private fun ChipContent(viewModel: OngoingActivityChipModel.Shown, modifier: Modifier = Modifier) {
-    val context = LocalContext.current
-    when (viewModel) {
-        is OngoingActivityChipModel.Shown.Timer -> {
-            val timerState = rememberChronometerState(startTimeMillis = viewModel.startTimeMs)
-            Text(
-                text = timerState.currentTimeText,
-                style = MaterialTheme.typography.labelLarge,
-                color = Color(viewModel.colors.text(context)),
-                modifier = modifier.neverDecreaseWidth(),
-            )
-        }
-
-        is OngoingActivityChipModel.Shown.Countdown -> {
-            ChipText(
-                text = viewModel.secondsUntilStarted.toString(),
-                color = Color(viewModel.colors.text(context)),
-                style = MaterialTheme.typography.labelLarge,
-                modifier = modifier.neverDecreaseWidth(),
-                backgroundColor = Color(viewModel.colors.background(context).defaultColor),
-            )
-        }
-
-        is OngoingActivityChipModel.Shown.Text -> {
-            ChipText(
-                text = viewModel.text,
-                color = Color(viewModel.colors.text(context)),
-                style = MaterialTheme.typography.labelLarge,
-                modifier = modifier,
-                backgroundColor = Color(viewModel.colors.background(context).defaultColor),
-            )
-        }
-
-        is OngoingActivityChipModel.Shown.ShortTimeDelta -> {
-            // TODO(b/372657935): Implement ShortTimeDelta content in compose.
-        }
-    }
-}
-
-@Composable
 private fun ExpandableChip(
     color: () -> Color,
     shape: Shape,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt
deleted file mode 100644
index 505a5fc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2025 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.chips.ui.compose.modifiers
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasureResult
-import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.node.LayoutModifierNode
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.constrain
-
-/** A modifier that ensures the width of the content only increases and never decreases. */
-fun Modifier.neverDecreaseWidth(): Modifier {
-    return this.then(neverDecreaseWidthElement)
-}
-
-private data object neverDecreaseWidthElement : ModifierNodeElement<NeverDecreaseWidthNode>() {
-    override fun create(): NeverDecreaseWidthNode {
-        return NeverDecreaseWidthNode()
-    }
-
-    override fun update(node: NeverDecreaseWidthNode) {
-        error("This should never be called")
-    }
-}
-
-private class NeverDecreaseWidthNode : Modifier.Node(), LayoutModifierNode {
-    private var minWidth = 0
-
-    override fun MeasureScope.measure(
-        measurable: Measurable,
-        constraints: Constraints,
-    ): MeasureResult {
-        val placeable = measurable.measure(Constraints(minWidth = minWidth).constrain(constraints))
-        val width = placeable.width
-        val height = placeable.height
-
-        minWidth = maxOf(minWidth, width)
-
-        return layout(width, height) { placeable.place(0, 0) }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 68c8f8c..e0c7645 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -40,7 +40,7 @@
     }
 
     /** This chip should be shown with the given information. */
-    abstract class Shown(
+    sealed class Shown(
         /** The icon to show on the chip. If null, no icon will be shown. */
         open val icon: ChipIcon?,
         /** What colors to use for the chip. */
@@ -170,5 +170,8 @@
 
         /** The chip expands into a dialog or activity on click. */
         data class ExpandAction(val onClick: (Expandable) -> Unit) : ClickBehavior
+
+        /** Clicking the chip will show the heads up notification associated with the chip. */
+        data class ShowHeadsUpNotification(val onClick: () -> Unit) : ClickBehavior
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelper.kt
index c850709..823b910 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelper.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -41,7 +40,6 @@
  * this class helps us immediately hide the chip as soon as the user clicks "Stop" in the dialog.
  * See b/353249803#comment4.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 class ChipTransitionHelper(@Application private val scope: CoroutineScope) {
     /** A flow that emits each time the user has clicked "Stop" on the dialog. */
     @SuppressLint("SharedFlowCreation")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 351cdc8..b5a781e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.data.StatusBarDataLayerModule
 import com.android.systemui.statusbar.data.repository.LightBarControllerStore
@@ -140,6 +141,20 @@
         @Provides
         @SysUISingleton
         @IntoMap
+        @ClassKey(ShareToAppChipViewModel::class)
+        fun providesShareToAppChipViewModel(
+            shareToAppChipViewModelLazy: Lazy<ShareToAppChipViewModel>
+        ): CoreStartable {
+            return if (com.android.media.projection.flags.Flags.showStopDialogPostCallEnd()) {
+                shareToAppChipViewModelLazy.get()
+            } else {
+                CoreStartable.NOP
+            }
+        }
+
+        @Provides
+        @SysUISingleton
+        @IntoMap
         @ClassKey(MultiDisplayStatusBarWindowControllerStore::class)
         fun multiDisplayControllerStoreAsCoreStartable(
             storeLazy: Lazy<MultiDisplayStatusBarWindowControllerStore>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt b/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt
index 2ae54d7..c9024d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt
@@ -33,7 +33,7 @@
     /** Is the refactor enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.statusBarNoHunBehavior() && android.app.Flags.notificationsRedesignAppIcons()
+        get() = Flags.statusBarNoHunBehavior()
 
     /**
      * Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 3825c09..b6ef958 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification
 
 import android.app.Notification
+import android.app.Notification.EXTRA_SUMMARIZED_CONTENT
 import android.content.Context
 import android.content.pm.LauncherApps
 import android.graphics.drawable.AnimatedImageDrawable
@@ -66,6 +67,12 @@
             messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo)
             shortcutInfo.label?.let { label -> messagingStyle.conversationTitle = label }
         }
+        if (NmSummarizationUiFlag.isEnabled) {
+            entry.sbn.notification.extras.putCharSequence(
+                EXTRA_SUMMARIZED_CONTENT, entry.ranking.summarization
+            )
+        }
+
         messagingStyle.unreadMessageCount =
             conversationNotificationManager.getUnreadCount(entry, recoveredBuilder)
         return messagingStyle
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt
new file mode 100644
index 0000000..feac0a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2025 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.notification
+
+import android.app.Flags;
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/**
+ * Helper for android.app.nm_summarization and android.nm_summarization_ui. The new functionality
+ * should be enabled if either flag is enabled.
+ */
+@Suppress("NOTHING_TO_INLINE")
+object NmSummarizationUiFlag {
+    const val FLAG_DESC = "android.app.nm_summarization(_ui)"
+
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.nmSummarizationUi() || Flags.nmSummarization()
+
+    /**
+     * 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_DESC)
+
+    /**
+     * 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_DESC)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 417e57d..5cc79df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -836,6 +836,14 @@
     }
 
     /**
+     * Returns whether the NotificationEntry is promoted ongoing.
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public boolean isOngoingPromoted() {
+        return mSbn.getNotification().isPromotedOngoing();
+    }
+
+    /**
      * Returns whether this row is considered blockable (i.e. it's not a system notif
      * or is not in an allowList).
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index 1875e7e..90916d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.statusbar.notification.collection.coordinator
 
 import android.app.Notification
@@ -48,7 +46,6 @@
 import dagger.Module
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.mapNotNull
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 2d1eccd..a0a8671 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -22,6 +22,7 @@
 import com.android.internal.widget.MessagingMessage
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Flags
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -144,7 +145,12 @@
         )
         log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
         traceSection("updateNotifOnUiModeChanged") {
-            mPipeline?.allNotifs?.forEach { entry -> entry.row?.onUiModeChanged() }
+            mPipeline?.allNotifs?.forEach { entry ->
+                entry.row?.onUiModeChanged()
+                if (Flags.notificationUndoGutsOnConfigChanged()) {
+                    mGutsManager.closeAndUndoGuts()
+                }
+            }
         }
     }
 
@@ -152,9 +158,15 @@
         colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
         mPipeline?.allNotifs?.forEach { entry ->
             entry.onDensityOrFontScaleChanged()
-            val exposedGuts = entry.areGutsExposed()
-            if (exposedGuts) {
-                mGutsManager.onDensityOrFontScaleChanged(entry)
+            if (Flags.notificationUndoGutsOnConfigChanged()) {
+                mGutsManager.closeAndUndoGuts()
+            } else {
+                // This property actually gets reset when the guts are re-inflated, so we're never
+                // actually calling onDensityOrFontScaleChanged below.
+                val exposedGuts = entry.areGutsExposed()
+                if (exposedGuts) {
+                    mGutsManager.onDensityOrFontScaleChanged(entry)
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
index 331ef1c..aa5008b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
@@ -40,6 +40,7 @@
     @RedactionType val redactionType: Int,
     val isChildInGroup: Boolean,
     val isGroupSummary: Boolean,
+    val summarization: String?,
 ) {
     companion object {
         @JvmStatic
@@ -61,6 +62,7 @@
                 AsyncGroupHeaderViewInflation.isEnabled &&
                     !oldAdjustment.isGroupSummary &&
                     newAdjustment.isGroupSummary -> true
+                oldAdjustment.summarization != newAdjustment.summarization -> true
                 else -> false
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 97e55c1..465bc28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -152,5 +152,6 @@
                 },
             isChildInGroup = entry.hasEverBeenGroupChild(),
             isGroupSummary = entry.hasEverBeenGroupSummary(),
+            summarization = entry.ranking.summarization
         )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 0346108..3dbf069 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -27,6 +27,7 @@
 import com.android.settingslib.notification.domain.interactor.NotificationsSoundPolicyInteractor;
 import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Application;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -84,6 +85,8 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule;
+import com.android.systemui.statusbar.notification.stack.MagneticNotificationRowManager;
+import com.android.systemui.statusbar.notification.stack.MagneticNotificationRowManagerImpl;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -322,4 +325,19 @@
             return (entry, recoveredBuilder) -> null;
         }
     }
+
+    /**
+     * Provide an implementation of {@link MagneticNotificationRowManager} based on its flag.
+     */
+    @Provides
+    @SysUISingleton
+    static MagneticNotificationRowManager provideMagneticNotificationRowManager(
+            Provider<MagneticNotificationRowManagerImpl> implProvider
+    ) {
+        if (Flags.magneticNotificationSwipes()) {
+            return implProvider.get();
+        } else {
+            return MagneticNotificationRowManager.getEmpty();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
index 6140c92..cdbe0fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.statusbar.notification.domain.interactor
 
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
@@ -29,7 +27,6 @@
 import com.android.systemui.statusbar.notification.headsup.PinnedStatus
 import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
index 7e2361f..fa0cea1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.icu.text.MessageFormat
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.modes.shared.ModesUi
@@ -36,9 +37,11 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
 
 /**
  * ViewModel for the empty shade (aka the "No notifications" text shown when there are no
@@ -51,6 +54,7 @@
     zenModeInteractor: ZenModeInteractor,
     seenNotificationsInteractor: SeenNotificationsInteractor,
     notificationSettingsInteractor: NotificationSettingsInteractor,
+    configurationInteractor: ConfigurationInteractor,
     @Background bgDispatcher: CoroutineDispatcher,
     dumpManager: DumpManager,
 ) : FlowDumperImpl(dumpManager) {
@@ -71,6 +75,13 @@
             "hasFilteredOutSeenNotifications"
         )
 
+    private val primaryLocale by lazy {
+        configurationInteractor.configurationValues
+            .map { it.locales.get(0) ?: Locale.getDefault() }
+            .onStart { emit(Locale.getDefault()) }
+            .distinctUntilChanged()
+    }
+
     val text: Flow<String> by lazy {
         if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode()) {
             flowOf(context.getString(R.string.empty_shade_text))
@@ -79,14 +90,16 @@
             // recommended architecture, and making it so it reacts to changes for the new Modes.
             // The former does not depend on the modes flags being on, but the latter does.
             if (ModesUi.isEnabled) {
-                    zenModeInteractor.modesHidingNotifications.map { modes ->
+                    combine(zenModeInteractor.modesHidingNotifications, primaryLocale) {
+                        modes,
+                        locale ->
                         // Create a string that is either "No notifications" if no modes are
-                        // filtering
-                        // them out, or something like "Notifications paused by SomeMode" otherwise.
+                        // filtering them out, or something like "Notifications paused by SomeMode"
+                        // otherwise.
                         val msgFormat =
                             MessageFormat(
                                 context.getString(R.string.modes_suppressing_shade_text),
-                                Locale.getDefault(),
+                                locale,
                             )
                         val count = modes.count()
                         val args: MutableMap<String, Any> = HashMap()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index d401283..96192b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -19,6 +19,7 @@
 import static android.graphics.PorterDuff.Mode.SRC_ATOP;
 
 import static com.android.systemui.Flags.notificationFooterBackgroundTintOptimization;
+import static com.android.systemui.Flags.notificationShadeBlur;
 import static com.android.systemui.util.ColorUtilKt.hexColorString;
 
 import android.annotation.ColorInt;
@@ -29,6 +30,7 @@
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
@@ -39,6 +41,8 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.common.shared.colors.SurfaceEffectColors;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
@@ -383,9 +387,23 @@
         final Drawable historyBg = NotifRedesignFooter.isEnabled()
                 ? theme.getDrawable(R.drawable.notif_footer_btn_background) : null;
         final @ColorInt int scHigh;
+
         if (!notificationFooterBackgroundTintOptimization()) {
-            scHigh = mContext.getColor(
-                    com.android.internal.R.color.materialColorSurfaceContainerHigh);
+            if (notificationShadeBlur()) {
+                Color backgroundColor = Color.valueOf(
+                        SurfaceEffectColors.surfaceEffect0(getResources()));
+                scHigh = ColorUtils.setAlphaComponent(backgroundColor.toArgb(), 0xFF);
+                // Apply alpha on background drawables.
+                int backgroundAlpha = (int) (backgroundColor.alpha() * 0xFF);
+                clearAllBg.setAlpha(backgroundAlpha);
+                settingsBg.setAlpha(backgroundAlpha);
+                if (historyBg != null) {
+                    historyBg.setAlpha(backgroundAlpha);
+                }
+            } else {
+                scHigh = mContext.getColor(
+                        com.android.internal.R.color.materialColorSurfaceContainerHigh);
+            }
             if (scHigh != 0) {
                 final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
                 clearAllBg.setColorFilter(bgColorFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
index 0171fb7..be61dc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
@@ -706,7 +706,7 @@
     }
 
     private void updateHeadsUpFlow() {
-        mHeadsUpNotificationRows.setValue(new HashSet<>(getHeadsUpEntryPhoneMap().values()));
+        mHeadsUpNotificationRows.setValue(new HashSet<>(mHeadsUpEntryMap.values()));
     }
 
     @Override
@@ -732,11 +732,6 @@
         return mHeadsUpAnimatingAway.getValue();
     }
 
-    @NonNull
-    private ArrayMap<String, HeadsUpEntry> getHeadsUpEntryPhoneMap() {
-        return mHeadsUpEntryMap;
-    }
-
     /**
      * Called to notify the listeners that the HUN animating away animation has ended.
      */
@@ -1014,7 +1009,7 @@
     @Override
     public void setRemoteInputActive(
             @NonNull NotificationEntry entry, boolean remoteInputActive) {
-        HeadsUpEntry headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
+        HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(entry.getKey());
         if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) {
             headsUpEntry.mRemoteInputActive = remoteInputActive;
             if (ExpandHeadsUpOnInlineReply.isEnabled() && remoteInputActive) {
@@ -1029,11 +1024,6 @@
         }
     }
 
-    @Nullable
-    private HeadsUpEntry getHeadsUpEntryPhone(@NonNull String key) {
-        return mHeadsUpEntryMap.get(key);
-    }
-
     @Override
     public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
@@ -1125,7 +1115,7 @@
             return true;
         }
 
-        HeadsUpEntry headsUpEntry = getHeadsUpEntryPhone(key);
+        HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
         HeadsUpEntry topEntry = getTopHeadsUpEntryPhone();
 
         if (headsUpEntry == null || headsUpEntry != topEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
index b913355..f02edee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
@@ -13,8 +13,6 @@
  *
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.statusbar.notification.icon.domain.interactor
 
 import com.android.systemui.dagger.qualifiers.Background
@@ -29,7 +27,6 @@
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlin.jvm.optionals.getOrNull
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 664b2af..33c71d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -27,10 +27,22 @@
 import android.widget.ImageView
 import android.widget.ProgressBar
 import android.widget.TextView
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.key
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.core.view.isVisible
+import com.android.app.tracing.traceSection
 import com.android.internal.R
 import com.android.internal.widget.BigPictureNotificationImageView
 import com.android.internal.widget.CachingIconView
@@ -40,6 +52,8 @@
 import com.android.internal.widget.NotificationRowIconView
 import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.res.R as systemuiR
+import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.PrimaryText
+import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.SecondaryText
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
@@ -51,44 +65,81 @@
         return
     }
 
-    val viewModel =
-        rememberViewModel(traceName = "AODPromotedNotification") { viewModelFactory.create() }
+    val viewModel = rememberViewModel(traceName = "$TAG.viewModel") { viewModelFactory.create() }
 
     val content = viewModel.content ?: return
 
     key(content.identity) {
-        AndroidView(
-            factory = { context ->
-                LayoutInflater.from(context)
-                    .inflate(content.layoutResource, /* root= */ null)
-                    .apply { setTag(viewUpdaterTagId, AODPromotedNotificationViewUpdater(this)) }
-            },
-            update = { view ->
-                (view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater).update(
-                    content
-                )
-            },
-        )
+        val layoutResource = content.layoutResource ?: return
+
+        val topPadding = dimensionResource(systemuiR.dimen.below_clock_padding_start_icons)
+        val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
+        val paddingValues =
+            PaddingValues(top = topPadding, start = sidePaddings, end = sidePaddings, bottom = 0.dp)
+
+        val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
+
+        val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
+        val borderShape = RoundedCornerShape(borderRadius)
+
+        Box(modifier = Modifier.padding(paddingValues)) {
+            AODPromotedNotificationView(
+                layoutResource = layoutResource,
+                content = content,
+                modifier = Modifier.border(borderStroke, borderShape),
+            )
+        }
     }
 }
 
-private val PromotedNotificationContentModel.layoutResource: Int
+@Composable
+fun AODPromotedNotificationView(
+    layoutResource: Int,
+    content: PromotedNotificationContentModel,
+    modifier: Modifier = Modifier,
+) {
+    AndroidView(
+        factory = { context ->
+            val view =
+                traceSection("$TAG.inflate") {
+                    LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
+                }
+
+            val updater =
+                traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(view) }
+
+            view.setTag(viewUpdaterTagId, updater)
+
+            view
+        },
+        update = { view ->
+            val updater = view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
+
+            traceSection("$TAG.update") { updater.update(content) }
+        },
+        modifier = modifier,
+    )
+}
+
+private val PromotedNotificationContentModel.layoutResource: Int?
     get() {
         return if (Flags.notificationsRedesignTemplates()) {
             when (style) {
+                Style.Base -> R.layout.notification_2025_template_expanded_base
                 Style.BigPicture -> R.layout.notification_2025_template_expanded_big_picture
                 Style.BigText -> R.layout.notification_2025_template_expanded_big_text
                 Style.Call -> R.layout.notification_2025_template_expanded_call
                 Style.Progress -> R.layout.notification_2025_template_expanded_progress
-                Style.Ineligible -> 0
+                Style.Ineligible -> null
             }
         } else {
             when (style) {
+                Style.Base -> R.layout.notification_template_material_big_base
                 Style.BigPicture -> R.layout.notification_template_material_big_picture
                 Style.BigText -> R.layout.notification_template_material_big_text
                 Style.Call -> R.layout.notification_template_material_big_call
                 Style.Progress -> R.layout.notification_template_material_progress
-                Style.Ineligible -> 0
+                Style.Ineligible -> null
             }
         }
     }
@@ -131,6 +182,7 @@
 
     fun update(content: PromotedNotificationContentModel) {
         when (content.style) {
+            Style.Base -> updateBase(content)
             Style.BigPicture -> updateBigPicture(content)
             Style.BigText -> updateBigText(content)
             Style.Call -> updateCall(content)
@@ -139,20 +191,24 @@
         }
     }
 
-    private fun updateBigPicture(content: PromotedNotificationContentModel) {
+    private fun updateBase(
+        content: PromotedNotificationContentModel,
+        textView: ImageFloatingTextView? = null,
+    ) {
         updateHeader(content)
 
         updateTitle(title, content)
-        updateText(text, content)
+        updateText(textView ?: text, content)
+    }
+
+    private fun updateBigPicture(content: PromotedNotificationContentModel) {
+        updateBase(content)
 
         bigPicture?.visibility = GONE
     }
 
     private fun updateBigText(content: PromotedNotificationContentModel) {
-        updateHeader(content)
-
-        updateTitle(title, content)
-        updateText(bigText, content)
+        updateBase(content, textView = bigText)
     }
 
     private fun updateCall(content: PromotedNotificationContentModel) {
@@ -162,10 +218,7 @@
     }
 
     private fun updateProgress(content: PromotedNotificationContentModel) {
-        updateHeader(content)
-
-        updateTitle(title, content)
-        updateText(text, content)
+        updateBase(content)
 
         updateNewProgressBar(content)
     }
@@ -246,12 +299,12 @@
     }
 
     private fun updateTitle(titleView: TextView?, content: PromotedNotificationContentModel) {
-        updateTextView(titleView, content.title, color = Color.PrimaryText)
+        updateTextView(titleView, content.title, color = PrimaryText)
     }
 
     private fun updateTimeAndChronometer(content: PromotedNotificationContentModel) {
-        setTextViewColor(time, Color.SecondaryText)
-        setTextViewColor(chronometer, Color.SecondaryText)
+        setTextViewColor(time, SecondaryText)
+        setTextViewColor(chronometer, SecondaryText)
 
         val timeValue = content.time
 
@@ -293,7 +346,7 @@
     private fun updateTextView(
         view: TextView?,
         text: CharSequence?,
-        color: Color = Color.SecondaryText,
+        color: AodPromotedNotificationColor = SecondaryText,
     ) {
         setTextViewColor(view, color)
 
@@ -306,15 +359,21 @@
         }
     }
 
-    private fun setTextViewColor(view: TextView?, color: Color) {
-        view?.setTextColor(color.color.toInt())
-    }
-
-    private enum class Color(val color: UInt) {
-        Background(0x00000000u),
-        PrimaryText(0xFFFFFFFFu),
-        SecondaryText(0xFFCCCCCCu),
+    private fun setTextViewColor(view: TextView?, color: AodPromotedNotificationColor) {
+        view?.setTextColor(color.colorInt)
     }
 }
 
+private enum class AodPromotedNotificationColor(colorUInt: UInt) {
+    Background(0x00000000u),
+    PrimaryText(0xFFFFFFFFu),
+    SecondaryText(0xFFCCCCCCu);
+
+    val colorInt = colorUInt.toInt()
+    val color = Color(colorInt)
+    val brush = SolidColor(color)
+}
+
 private val viewUpdaterTagId = systemuiR.id.aod_promoted_notification_view_updater_tag
+
+private const val TAG = "AODPromotedNotification"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AutomaticPromotionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AutomaticPromotionCoordinator.kt
index 3957462..2af2068 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AutomaticPromotionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AutomaticPromotionCoordinator.kt
@@ -30,6 +30,14 @@
          * (but not normally promoted notifications).
          */
         const val EXTRA_WAS_AUTOMATICALLY_PROMOTED = "android.wasAutomaticallyPromoted"
+
+        /**
+         * An extra set only on automatically promoted notifications that contains text that could
+         * reasonably be the short critical text. For now, we're only extracting arrival times. Will
+         * be set as a String.
+         */
+        const val EXTRA_AUTOMATICALLY_EXTRACTED_SHORT_CRITICAL_TEXT =
+            "android.automaticallyExtractedShortCriticalText"
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
index df2eb08..24d071c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -26,9 +26,11 @@
 import android.app.Notification.EXTRA_TITLE
 import android.app.Notification.ProgressStyle
 import android.content.Context
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_AUTOMATICALLY_EXTRACTED_SHORT_CRITICAL_TEXT
 import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Companion.isPromotedForStatusBarChip
@@ -96,8 +98,7 @@
                 primaryTextColor = colorsFromNotif.primaryTextColor,
             )
 
-        recoveredBuilder.style?.extractContent(contentBuilder)
-            ?: run { contentBuilder.style = Style.Ineligible }
+        recoveredBuilder.extractStyleContent(contentBuilder)
 
         return contentBuilder.build().also { logger.logExtractionSucceeded(entry, it) }
     }
@@ -113,7 +114,13 @@
     if (!android.app.Flags.apiRichOngoing()) {
         return null
     }
-    return this.shortCriticalText
+    if (this.shortCriticalText != null) {
+        return this.shortCriticalText
+    }
+    if (Flags.promoteNotificationsAutomatically()) {
+        return this.extras?.getString(EXTRA_AUTOMATICALLY_EXTRACTED_SHORT_CRITICAL_TEXT)
+    }
+    return null
 }
 
 private fun Notification.chronometerCountDown(): Boolean =
@@ -132,28 +139,32 @@
     }
 }
 
-private fun Notification.Style.extractContent(
+private fun Notification.Builder.extractStyleContent(
     contentBuilder: PromotedNotificationContentModel.Builder
 ) {
+    val style = this.style
+
     contentBuilder.style =
-        when (this) {
+        when (style) {
+            null -> Style.Base
+
             is BigPictureStyle -> {
-                extractContent(contentBuilder)
+                style.extractContent(contentBuilder)
                 Style.BigPicture
             }
 
             is BigTextStyle -> {
-                extractContent(contentBuilder)
+                style.extractContent(contentBuilder)
                 Style.BigText
             }
 
             is CallStyle -> {
-                extractContent(contentBuilder)
+                style.extractContent(contentBuilder)
                 Style.Call
             }
 
             is ProgressStyle -> {
-                extractContent(contentBuilder)
+                style.extractContent(contentBuilder)
                 Style.Progress
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
index a175f90..3dacae2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -131,6 +131,7 @@
 
     /** The promotion-eligible style of a notification, or [Style.Ineligible] if not. */
     enum class Style {
+        Base, // style == null
         BigPicture,
         BigText,
         Call,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfo.java
index aad618d..9c6e41c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfo.java
@@ -64,11 +64,11 @@
             boolean isNonblockable,
             boolean wasShownHighPriority,
             AssistantFeedbackController assistantFeedbackController,
-            MetricsLogger metricsLogger) throws RemoteException {
+            MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException {
         super.bindNotification(pm, iNotificationManager, onUserInteractionCallback,
                 channelEditorDialogController, pkg, notificationChannel, entry, onSettingsClick,
                 onAppSettingsClick, uiEventLogger, isDeviceProvisioned, isNonblockable,
-                wasShownHighPriority, assistantFeedbackController, metricsLogger);
+                wasShownHighPriority, assistantFeedbackController, metricsLogger, onCloseClick);
 
         // Additionally, bind the feedback button.
         ComponentName assistant = iNotificationManager.getAllowedNotificationAssistant();
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 ee8f9ea..a6dde10 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
@@ -20,8 +20,8 @@
 import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 
-import static com.android.systemui.flags.Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE;
 import static com.android.systemui.Flags.notificationsPinnedHunInShade;
+import static com.android.systemui.flags.Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE;
 import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
 import static com.android.systemui.statusbar.policy.RemoteInputView.FOCUS_ANIMATION_MIN_SCALE;
@@ -69,6 +69,9 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
@@ -107,6 +110,7 @@
 import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
 import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
@@ -118,6 +122,7 @@
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.MagneticRowListener;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -130,6 +135,7 @@
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.DumpUtilsKt;
 import com.android.systemui.util.ListenerSet;
+import com.android.wm.shell.shared.animation.PhysicsAnimator;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -164,6 +170,8 @@
     private boolean mShowSnooze = false;
     private boolean mIsFaded;
 
+    private boolean mIsPromotedOngoing = false;
+
     /**
      * Listener for when {@link ExpandableNotificationRow} is laid out.
      */
@@ -197,6 +205,7 @@
     private int mMaxSmallHeightBeforeS;
     private int mMaxSmallHeight;
     private int mMaxExpandedHeight;
+    private int mMaxExpandedHeightForPromotedOngoing;
     private int mNotificationLaunchHeight;
     private boolean mMustStayOnScreen;
 
@@ -332,6 +341,15 @@
     private boolean mSaveSpaceOnLockscreen;
 
     /**
+     * It is added for unit testing purpose.
+     * Please do not use it for other purposes.
+     */
+    @VisibleForTesting
+    public void setIgnoreLockscreenConstraints(boolean ignoreLockscreenConstraints) {
+        mIgnoreLockscreenConstraints = ignoreLockscreenConstraints;
+    }
+
+    /**
      * True if we use intrinsic height regardless of vertical space available on lockscreen.
      */
     private boolean mIgnoreLockscreenConstraints;
@@ -343,6 +361,45 @@
         }
     };
 
+    private final SpringAnimation mMagneticAnimator = new SpringAnimation(
+            this, FloatPropertyCompat.createFloatPropertyCompat(TRANSLATE_CONTENT));
+
+    private final MagneticRowListener mMagneticRowListener = new MagneticRowListener() {
+
+        @Override
+        public void setMagneticTranslation(float translation) {
+            if (mMagneticAnimator.isRunning()) {
+                mMagneticAnimator.animateToFinalPosition(translation);
+            } else {
+                setTranslation(translation);
+            }
+        }
+
+        @Override
+        public void triggerMagneticForce(float endTranslation, @NonNull SpringForce springForce,
+                float startVelocity) {
+            cancelMagneticAnimations();
+            mMagneticAnimator.setSpring(springForce);
+            mMagneticAnimator.setStartVelocity(startVelocity);
+            mMagneticAnimator.animateToFinalPosition(endTranslation);
+        }
+
+        @Override
+        public void cancelMagneticAnimations() {
+            cancelSnapBackAnimation();
+            cancelTranslateAnimation();
+            mMagneticAnimator.cancel();
+        }
+    };
+
+    private void cancelSnapBackAnimation() {
+        PhysicsAnimator<ExpandableNotificationRow> animator =
+                PhysicsAnimator.getInstanceIfExists(this /* target */);
+        if (animator != null) {
+            animator.cancel();
+        }
+    }
+
     /**
      * Toggles expansion state.
      */
@@ -805,6 +862,13 @@
     }
 
     private void updateLimitsForView(NotificationContentView layout) {
+        final int maxExpandedHeight;
+        if (isPromotedOngoing()) {
+            maxExpandedHeight = mMaxExpandedHeightForPromotedOngoing;
+        } else {
+            maxExpandedHeight = mMaxExpandedHeight;
+        }
+
         View contractedView = layout.getContractedChild();
         boolean customView = contractedView != null
                 && contractedView.getId()
@@ -825,7 +889,7 @@
                 smallHeight = mMaxSmallHeightBeforeS;
             }
         } else if (isCallLayout) {
-            smallHeight = mMaxExpandedHeight;
+            smallHeight = maxExpandedHeight;
         } else {
             smallHeight = mMaxSmallHeight;
         }
@@ -849,7 +913,8 @@
         if (headsUpWrapper != null) {
             headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight());
         }
-        layout.setHeights(smallHeight, headsUpHeight, mMaxExpandedHeight);
+
+        layout.setHeights(smallHeight, headsUpHeight, maxExpandedHeight);
     }
 
     @NonNull
@@ -1182,15 +1247,6 @@
     }
 
     /**
-     * Prepares expansion changed.
-     */
-    public void prepareExpansionChanged() {
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.prepareExpansionChanged();
-        }
-    }
-
-    /**
      * Starts child animations.
      */
     public void startChildAnimation(AnimationProperties properties) {
@@ -1259,6 +1315,9 @@
         if (mIsSummaryWithChildren) {
             return mChildrenContainer.getIntrinsicHeight();
         }
+        if (isPromotedOngoing()) {
+            return getMaxExpandHeight();
+        }
         if (mExpandedWhenPinned) {
             return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
         } else if (android.app.Flags.compactHeadsUpNotification()
@@ -1501,7 +1560,7 @@
         // Let's update our childrencontainer. This is intentionally not guarded with
         // mIsSummaryWithChildren since we might have had children but not anymore.
         if (mChildrenContainer != null) {
-            mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.getSbn());
+            mChildrenContainer.reInflateViews(mExpandClickListener);
         }
         if (mGuts != null) {
             NotificationGuts oldGuts = mGuts;
@@ -2078,6 +2137,8 @@
         }
         mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_height);
+        mMaxExpandedHeightForPromotedOngoing = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_max_height_for_promoted_ongoing);
         mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_heads_up_height_legacy);
         mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
@@ -2763,6 +2824,9 @@
         if (mIsSummaryWithChildren && !shouldShowPublic()) {
             return !mChildrenExpanded;
         }
+        if (isPromotedOngoing()) {
+            return false;
+        }
         return mEnableNonGroupedNotificationExpand && mExpandable;
     }
 
@@ -2771,6 +2835,18 @@
         mPrivateLayout.updateExpandButtons(isExpandable());
     }
 
+    /**
+     * Set this notification to be promoted ongoing
+     */
+    public void setPromotedOngoing(boolean promotedOngoing) {
+        if (PromotedNotificationUiForceExpanded.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
+
+        mIsPromotedOngoing = promotedOngoing;
+        setExpandable(!mIsPromotedOngoing);
+    }
+
     @Override
     public void setClipToActualHeight(boolean clipToActualHeight) {
         super.setClipToActualHeight(clipToActualHeight || isUserLocked());
@@ -2840,6 +2916,8 @@
     }
 
     public void setUserLocked(boolean userLocked) {
+        if (isPromotedOngoing()) return;
+
         mUserLocked = userLocked;
         mPrivateLayout.setUserExpanding(userLocked);
         // This is intentionally not guarded with mIsSummaryWithChildren since we might have had
@@ -3001,6 +3079,35 @@
         }
     }
 
+    public boolean isPromotedOngoing() {
+        return PromotedNotificationUiForceExpanded.isEnabled() && mIsPromotedOngoing;
+    }
+
+    private boolean isPromotedNotificationExpanded(boolean allowOnKeyguard) {
+        // public view in non group notifications is always collapsed.
+        if (shouldShowPublic()) {
+            return false;
+        }
+        // RON will always be expanded when it is not on keyguard.
+        if (!mOnKeyguard) {
+            return true;
+        }
+        // RON will always be expanded when it is allowed on keyguard.
+        // allowOnKeyguard is used for getting the maximum height by NotificationContentView and
+        // NotificationChildrenContainer.
+        if (allowOnKeyguard) {
+            return true;
+        }
+
+        // RON will be expanded when it needs to ignore lockscreen constraints.
+        if (mIgnoreLockscreenConstraints) {
+            return true;
+        }
+
+        // RON will need be collapsed when it needs to save space on the lock screen.
+        return !mSaveSpaceOnLockscreen;
+    }
+
     /**
      * Check whether the view state is currently expanded. This is given by the system in {@link
      * #setSystemExpanded(boolean)} and can be overridden by user expansion or
@@ -3014,6 +3121,10 @@
     }
 
     public boolean isExpanded(boolean allowOnKeyguard) {
+        if (isPromotedOngoing()) {
+            return isPromotedNotificationExpanded(allowOnKeyguard);
+        }
+
         return (!shouldShowPublic()) && (!mOnKeyguard || allowOnKeyguard)
                 && (!hasUserChangedExpansion()
                 && (isSystemExpanded() || isSystemChildExpanded())
@@ -4015,6 +4126,11 @@
                     + (!shouldShowPublic() && mIsSummaryWithChildren));
             pw.print(", mShowNoBackground: " + mShowNoBackground);
             pw.print(", clipBounds: " + getClipBounds());
+            if (PromotedNotificationUiForceExpanded.isEnabled()) {
+                pw.print(", isPromotedOngoing: " + isPromotedOngoing());
+                pw.print(", isExpandable: " + isExpandable());
+                pw.print(", mExpandable: " + mExpandable);
+            }
 
             pw.println();
             if (NotificationContentView.INCLUDE_HEIGHTS_TO_DUMP) {
@@ -4204,4 +4320,8 @@
         }
         mLogger.logRemoveTransientRow(row.getEntry(), getEntry());
     }
+
+    public MagneticRowListener getMagneticRowListener() {
+        return mMagneticRowListener;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index 1ff0d92..92c10ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -288,14 +288,21 @@
     public void setText(
             CharSequence titleText,
             CharSequence contentText,
-            CharSequence conversationSenderName
+            CharSequence conversationSenderName,
+            @Nullable String summarization
     ) {
         if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return;
-        if (conversationSenderName == null) {
+        if (summarization != null) {
             mConversationSenderName.setVisibility(GONE);
+            titleText = null;
+            contentText = summarization;
         } else {
-            mConversationSenderName.setVisibility(VISIBLE);
-            mConversationSenderName.setText(conversationSenderName);
+            if (conversationSenderName == null) {
+                mConversationSenderName.setVisibility(GONE);
+            } else {
+                mConversationSenderName.setVisibility(VISIBLE);
+                mConversationSenderName.setText(conversationSenderName);
+            }
         }
         // TODO (b/217799515): super.bind() doesn't use contentView, remove the contentView
         //  argument when the flag is removed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
index 793b3b8..6aa5e40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.row
 
+import android.animation.ValueAnimator
 import android.content.Context
 import android.graphics.BlendMode
 import android.graphics.Canvas
@@ -38,8 +39,8 @@
 import com.android.internal.graphics.ColorUtils
 import com.android.systemui.res.R
 import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
-import kotlin.math.max
-import kotlin.math.roundToInt
+import com.android.wm.shell.shared.animation.Interpolators
+import kotlin.math.min
 
 /**
  * A background style for smarter-smart-actions. The style is composed by a simplex3d noise,
@@ -48,7 +49,7 @@
 class MagicActionBackgroundDrawable(
     context: Context,
     primaryContainer: Int? = null,
-    private val seed: Float = 0f,
+    seed: Float = 0f,
 ) : Drawable() {
 
     private val pixelDensity = context.resources.displayMetrics.density
@@ -60,17 +61,15 @@
             .toFloat()
     private val buttonShape = Path()
     private val paddingVertical =
-        context.resources
-            .getDimensionPixelSize(R.dimen.smart_reply_button_padding_vertical)
-            .toFloat()
+        context.resources.getDimensionPixelSize(R.dimen.smart_action_button_icon_padding).toFloat()
 
     /** The color of the button background. */
     private val mainColor =
         primaryContainer
             ?: context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
 
-    /** Slightly dimmed down version of [mainColor] used on the simplex noise. */
-    private val dimColor: Int
+    /** Slightly brighter version of [mainColor] used on the simplex noise. */
+    private val effectColor: Int
         get() {
             val labColor = arrayOf(0.0, 0.0, 0.0).toDoubleArray()
             ColorUtils.colorToLAB(mainColor, labColor)
@@ -78,56 +77,97 @@
             return ColorUtils.CAMToColor(
                 camColor.hue,
                 camColor.chroma,
-                max(0f, (labColor[0] - 20).toFloat()),
+                min(100f, (labColor[0] + 10).toFloat()),
             )
         }
 
     private val bgShader = MagicActionBackgroundShader()
     private val bgPaint = Paint()
     private val outlinePaint = Paint()
+    private val gradientAnimator =
+        ValueAnimator.ofFloat(0f, 1f).apply {
+            duration = 2500
+            interpolator = Interpolators.LINEAR
+            addUpdateListener { invalidateSelf() }
+        }
+    private val turbulenceAnimator =
+        ValueAnimator.ofFloat(seed, seed + TURBULENCE_MOVEMENT).apply {
+            duration = ANIMATION_DURATION
+            interpolator = Interpolators.LINEAR
+            addUpdateListener { invalidateSelf() }
+            start()
+        }
+    private val effectFadeAnimation =
+        ValueAnimator.ofFloat(0f, 1f).apply {
+            duration = 1000
+            startDelay = ANIMATION_DURATION - 1000L
+            interpolator = Interpolators.STANDARD_DECELERATE
+            addUpdateListener { invalidateSelf() }
+        }
 
     init {
         bgShader.setColorUniform("in_color", mainColor)
-        bgShader.setColorUniform("in_dimColor", dimColor)
+        bgShader.setColorUniform("in_effectColor", effectColor)
         bgPaint.shader = bgShader
         outlinePaint.style = Paint.Style.STROKE
         // Stroke is doubled in width and then clipped, to avoid anti-aliasing artifacts at the edge
         // of the rectangle.
         outlinePaint.strokeWidth = outlineStrokeWidth * 2
         outlinePaint.blendMode = BlendMode.SCREEN
-        outlinePaint.alpha = (255 * 0.32f).roundToInt()
+        outlinePaint.alpha = OUTLINE_ALPHA
+
+        animate()
+    }
+
+    private fun animate() {
+        turbulenceAnimator.start()
+        gradientAnimator.start()
+        effectFadeAnimation.start()
     }
 
     override fun draw(canvas: Canvas) {
+        updateShaders()
+
         // We clip instead of drawing 2 rounded rects, otherwise there will be artifacts where
         // around the button background and the outline.
+        canvas.save()
         canvas.clipPath(buttonShape)
+        canvas.drawPath(buttonShape, bgPaint)
+        canvas.drawPath(buttonShape, outlinePaint)
+        canvas.restore()
+    }
 
-        canvas.drawRect(bounds, bgPaint)
-        canvas.drawRoundRect(
-            bounds.left.toFloat(),
-            bounds.top + paddingVertical,
-            bounds.right.toFloat(),
-            bounds.bottom - paddingVertical,
-            cornerRadius,
-            cornerRadius,
-            outlinePaint,
-        )
+    private fun updateShaders() {
+        val effectAlpha = 1f - effectFadeAnimation.animatedValue as Float
+        val turbulenceZ = turbulenceAnimator.animatedValue as Float
+        bgShader.setFloatUniform("in_sparkleMove", turbulenceZ * 1000)
+        bgShader.setFloatUniform("in_noiseMove", 0f, 0f, turbulenceZ)
+        bgShader.setFloatUniform("in_turbulenceAlpha", effectAlpha)
+        bgShader.setFloatUniform("in_spkarkleAlpha", SPARKLE_ALPHA * effectAlpha)
+        val gradientOffset = gradientAnimator.animatedValue as Float * bounds.width()
+        val outlineGradient =
+            LinearGradient(
+                gradientOffset + bounds.left.toFloat(),
+                0f,
+                gradientOffset + bounds.right.toFloat(),
+                0f,
+                mainColor,
+                ColorUtils.setAlphaComponent(mainColor, 0),
+                Shader.TileMode.MIRROR,
+            )
+        outlinePaint.shader = outlineGradient
     }
 
     override fun onBoundsChange(bounds: Rect) {
         super.onBoundsChange(bounds)
 
         val width = bounds.width().toFloat()
-        val height = bounds.height() - paddingVertical * 2
+        val height = bounds.height().toFloat()
         if (width == 0f || height == 0f) return
 
         bgShader.setFloatUniform("in_gridNum", NOISE_SIZE)
-        bgShader.setFloatUniform("in_spkarkleAlpha", SPARKLE_ALPHA)
-        bgShader.setFloatUniform("in_noiseMove", 0f, 0f, 0f)
         bgShader.setFloatUniform("in_size", width, height)
         bgShader.setFloatUniform("in_aspectRatio", width / height)
-        bgShader.setFloatUniform("in_time", seed)
         bgShader.setFloatUniform("in_pixelDensity", pixelDensity)
 
         buttonShape.reset()
@@ -140,18 +180,6 @@
             cornerRadius,
             Path.Direction.CW,
         )
-
-        val outlineGradient =
-            LinearGradient(
-                bounds.left.toFloat(),
-                0f,
-                bounds.right.toFloat(),
-                0f,
-                mainColor,
-                ColorUtils.setAlphaComponent(mainColor, 0),
-                Shader.TileMode.CLAMP,
-            )
-        outlinePaint.shader = outlineGradient
     }
 
     override fun setAlpha(alpha: Int) {
@@ -168,10 +196,15 @@
 
     companion object {
         /** Smoothness of the turbulence. Larger numbers yield more detail. */
-        private const val NOISE_SIZE = 0.7f
-
+        private const val NOISE_SIZE = 0.57f
         /** Strength of the sparkles overlaid on the turbulence. */
         private const val SPARKLE_ALPHA = 0.15f
+        /** Alpha (0..255) of the button outline */
+        private const val OUTLINE_ALPHA = 82
+        /** Turbulence grid size */
+        private const val TURBULENCE_MOVEMENT = 4.3f
+        /** Total animation duration in millis */
+        private const val ANIMATION_DURATION = 5000L
     }
 }
 
@@ -183,24 +216,25 @@
             """
             uniform float in_gridNum;
             uniform vec3 in_noiseMove;
+            uniform half in_sparkleMove;
             uniform vec2 in_size;
             uniform float in_aspectRatio;
-            uniform half in_time;
             uniform half in_pixelDensity;
+            uniform float in_turbulenceAlpha;
             uniform float in_spkarkleAlpha;
             layout(color) uniform vec4 in_color;
-            layout(color) uniform vec4 in_dimColor;
+            layout(color) uniform vec4 in_effectColor;
         """
         private const val MAIN_SHADER =
             """vec4 main(vec2 p) {
             vec2 uv = p / in_size.xy;
             uv.x *= in_aspectRatio;
             vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
-            half luma = 1.0 - getLuminosity(half3(simplex3d(noiseP)));
-            half4 turbulenceColor = mix(in_color, in_dimColor, luma);
-            float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time);
+            half luma = getLuminosity(half3(simplex3d(noiseP)));
+            half4 turbulenceColor = mix(in_color, in_effectColor, luma * in_turbulenceAlpha);
+            float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_sparkleMove);
             sparkle = min(sparkle * in_spkarkleAlpha, in_spkarkleAlpha);
-            return turbulenceColor + half4(half3(sparkle), 1.0);
+            return saturate(turbulenceColor + half4(sparkle));
         }
         """
         private const val SHADER = UNIFORMS + ShaderUtilLibrary.SHADER_LIB + MAIN_SHADER
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 57fe24f..13ed6c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -57,6 +57,7 @@
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
 import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
@@ -216,7 +217,8 @@
                             messagingStyle,
                             builder,
                             row.getContext(),
-                            false
+                            false,
+                            entry.getRanking().getSummarization()
                     );
             // If the messagingStyle is null, we want to inflate the normal view
             isConversation = viewModel.isConversation();
@@ -238,7 +240,8 @@
                                 messagingStyle,
                                 builder,
                                 row.getContext(),
-                                true);
+                                true,
+                                entry.getRanking().getSummarization());
             } else {
                 result.mPublicInflatedSingleLineViewModel =
                         SingleLineViewInflater.inflateRedactedSingleLineViewModel(
@@ -1111,6 +1114,10 @@
 
         entry.setHeadsUpStatusBarText(result.headsUpStatusBarText);
         entry.setHeadsUpStatusBarTextPublic(result.headsUpStatusBarTextPublic);
+        if (PromotedNotificationUiForceExpanded.isEnabled()) {
+            row.setPromotedOngoing(entry.isOngoingPromoted());
+        }
+
         Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row));
         if (endListener != null) {
             endListener.onAsyncInflationFinished(entry);
@@ -1313,7 +1320,8 @@
                                 messagingStyle,
                                 recoveredBuilder,
                                 mContext,
-                                false
+                                false,
+                                mEntry.getRanking().getSummarization()
                         );
                 result.mInflatedSingleLineView =
                         SingleLineViewInflater.inflatePrivateSingleLineView(
@@ -1333,7 +1341,8 @@
                                     messagingStyle,
                                     recoveredBuilder,
                                     mContext,
-                                    true
+                                    true,
+                                    null
                             );
                 } else {
                     result.mPublicInflatedSingleLineViewModel =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index b86d1d9..75d1c7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -287,7 +287,7 @@
      * @param save whether the state should be saved
      * @param force whether the guts should be force-closed regardless of state.
      */
-    private void closeControls(int x, int y, boolean save, boolean force) {
+    public void closeControls(int x, int y, boolean save, boolean force) {
         // First try to dismiss any blocking helper.
         if (getWindowToken() == null) {
             if (mClosedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 9712db8..445cd01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -48,6 +48,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.settingslib.notification.ConversationIconFactory;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -70,10 +71,10 @@
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.wmshell.BubblesManager;
 
@@ -223,6 +224,10 @@
     }
 
     public void onDensityOrFontScaleChanged(NotificationEntry entry) {
+        if (!Flags.notificationUndoGutsOnConfigChanged()) {
+            Log.wtf(TAG, "onDensityOrFontScaleChanged should not be called if"
+                    + " notificationUndoGutsOnConfigChanged is off");
+        }
         setExposedGuts(entry.getGuts());
         bindGuts(entry.getRow());
     }
@@ -422,7 +427,8 @@
                 row.getIsNonblockable(),
                 mHighPriorityProvider.isHighPriority(row.getEntry()),
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                row.getCloseButtonOnClickListener(row));
     }
 
     /**
@@ -476,7 +482,8 @@
                 row.getIsNonblockable(),
                 mHighPriorityProvider.isHighPriority(row.getEntry()),
                 mAssistantFeedbackController,
-                mMetricsLogger);
+                mMetricsLogger,
+                row.getCloseButtonOnClickListener(row));
     }
 
     /**
@@ -588,7 +595,8 @@
     }
 
     /**
-     * Closes guts or notification menus that might be visible and saves any changes.
+     * Closes guts or notification menus that might be visible and saves any changes if applicable
+     * (see {@link NotificationGuts.GutsContent#shouldBeSavedOnClose}).
      *
      * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
      * @param force true if guts should be closed regardless of state (used for snooze only).
@@ -609,6 +617,20 @@
     }
 
     /**
+     * Closes all guts that might be visible without saving changes.
+     */
+    public void closeAndUndoGuts() {
+        if (mNotificationGutsExposed != null) {
+            mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
+            mNotificationGutsExposed.closeControls(
+                    /* x = */ -1,
+                    /* y = */ -1,
+                    /* save = */ false,
+                    /* force = */ false);
+        }
+    }
+
+    /**
      * Returns the exposed NotificationGuts or null if none are exposed.
      */
     public NotificationGuts getExposedGuts() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 2012099..8d26f94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -202,7 +202,7 @@
             boolean isNonblockable,
             boolean wasShownHighPriority,
             AssistantFeedbackController assistantFeedbackController,
-            MetricsLogger metricsLogger)
+            MetricsLogger metricsLogger, OnClickListener onCloseClick)
             throws RemoteException {
         mINotificationManager = iNotificationManager;
         mMetricsLogger = metricsLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 6e8ec95..d213c78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -42,6 +42,7 @@
 
 import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Flags;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.AlphaOptimizedImageView;
@@ -263,6 +264,7 @@
         NotificationEntry entry = mParent.getEntry();
         int personNotifType = mPeopleNotificationIdentifier.getPeopleNotificationType(entry);
         if (android.app.Flags.notificationClassificationUi()
+                && entry.getChannel() != null
                 && SYSTEM_RESERVED_IDS.contains(entry.getChannel().getId())) {
             // Bundled notification; create bundle-specific guts.
             mInfoItem = createBundleItem(mContext);
@@ -270,6 +272,10 @@
             mInfoItem = createPartialConversationItem(mContext);
         } else if (personNotifType >= PeopleNotificationIdentifier.TYPE_FULL_PERSON) {
             mInfoItem = createConversationItem(mContext);
+        } else if (android.app.Flags.uiRichOngoing()
+                && Flags.permissionHelperUiRichOngoing()
+                && entry.getSbn().getNotification().isPromotedOngoing()) {
+            mInfoItem = createPromotedItem(mContext);
         } else {
             mInfoItem = createInfoItem(mContext);
         }
@@ -682,6 +688,16 @@
                 R.drawable.ic_settings);
     }
 
+    static NotificationMenuItem createPromotedItem(Context context) {
+        Resources res = context.getResources();
+        String infoDescription = res.getString(R.string.notification_menu_gear_description);
+        PromotedNotificationInfo infoContent =
+                (PromotedNotificationInfo) LayoutInflater.from(context).inflate(
+                        R.layout.promoted_notification_info, null, false);
+        return new NotificationMenuItem(context, infoDescription, infoContent,
+                R.drawable.ic_settings);
+    }
+
     static NotificationMenuItem createBundleItem(Context context) {
         Resources res = context.getResources();
         String infoDescription = res.getString(R.string.notification_menu_gear_description);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 0b299d9..f4aae6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -719,6 +719,7 @@
                         builder = builder,
                         systemUiContext = systemUiContext,
                         redactText = false,
+                        summarization = entry.ranking.summarization
                     )
                 } else null
 
@@ -735,6 +736,7 @@
                             builder = builder,
                             systemUiContext = systemUiContext,
                             redactText = true,
+                            summarization = null
                         )
                     } else {
                         SingleLineViewInflater.inflateRedactedSingleLineViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
index 6e78f56..a0b0415 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.log.dagger.NotificationRenderLog
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.logKey
+import com.android.systemui.statusbar.notification.stack.MagneticNotificationRowManagerImpl
 import javax.inject.Inject
 
 class NotificationRowLogger
@@ -203,6 +204,21 @@
             { "onAppearAnimationFinished childKey: $str1 isAppear:$bool1 cancelled:$bool2" },
         )
     }
+
+    fun logMagneticAndRoundableTargetsNotSet(
+        state: MagneticNotificationRowManagerImpl.State,
+        entry: NotificationEntry,
+    ) {
+        buffer.log(
+            TAG,
+            LogLevel.ERROR,
+            {
+                str1 = entry.logKey
+                str2 = state.name
+            },
+            { "Failed to set magnetic and roundable targets for $str1 on state $str2." },
+        )
+    }
 }
 
 private const val TAG = "NotifRow"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 99a6f6a..83897f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -51,6 +51,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Flags;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.res.R;
@@ -86,18 +87,26 @@
     private NotificationSwipeActionHelper mSnoozeListener;
     private StatusBarNotification mSbn;
 
-    private View mSnoozeView;
-    private TextView mSelectedOptionText;
+    @VisibleForTesting
+    public View mSnoozeView;
+    @VisibleForTesting
+    public TextView mSelectedOptionText;
     private TextView mUndoButton;
-    private ImageView mExpandButton;
-    private View mDivider;
-    private ViewGroup mSnoozeOptionContainer;
-    private List<SnoozeOption> mSnoozeOptions;
+    @VisibleForTesting
+    public ImageView mExpandButton;
+    @VisibleForTesting
+    public View mDivider;
+    @VisibleForTesting
+    public ViewGroup mSnoozeOptionContainer;
+    @VisibleForTesting
+    public List<SnoozeOption> mSnoozeOptions;
     private int mCollapsedHeight;
     private SnoozeOption mDefaultOption;
-    private SnoozeOption mSelectedOption;
+    @VisibleForTesting
+    public SnoozeOption mSelectedOption;
     private boolean mSnoozing;
-    private boolean mExpanded;
+    @VisibleForTesting
+    public boolean mExpanded;
     private AnimatorSet mExpandAnimation;
     private KeyValueListParser mParser;
 
@@ -334,7 +343,8 @@
         }
     }
 
-    private void showSnoozeOptions(boolean show) {
+    @VisibleForTesting
+    public void showSnoozeOptions(boolean show) {
         int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification
                 : com.android.internal.R.drawable.ic_expand_notification;
         mExpandButton.setImageResource(drawableId);
@@ -381,7 +391,8 @@
         mExpandAnimation.start();
     }
 
-    private void setSelected(SnoozeOption option, boolean userAction) {
+    @VisibleForTesting
+    public void setSelected(SnoozeOption option, boolean userAction) {
         if (option != mSelectedOption) {
             mSelectedOption = option;
             mSelectedOptionText.setText(option.getConfirmation());
@@ -466,7 +477,12 @@
 
     @Override
     public boolean handleCloseControls(boolean save, boolean force) {
-        if (mExpanded && !force) {
+        if (Flags.notificationUndoGutsOnConfigChanged() && !save) {
+            // Undo changes and let the guts handle closing the view
+            mSelectedOption = null;
+            showSnoozeOptions(false);
+            return false;
+        } else if (mExpanded && !force) {
             // Collapse expanded state on outside touch
             showSnoozeOptions(false);
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
new file mode 100644
index 0000000..e4a0fa5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
@@ -0,0 +1,104 @@
+/*
+ * 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.notification.row;
+
+import android.app.INotificationManager;
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.AssistantFeedbackController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * The guts of a notification revealed when performing a long press, specifically
+ * for notifications that are shown as promoted. Contains extra controls to allow user to revoke
+ * app permissions for sending promoted notifications.
+ */
+public class PromotedNotificationInfo extends NotificationInfo {
+    private static final String TAG = "PromotedNotifInfoGuts";
+    private INotificationManager mNotificationManager;
+
+    public PromotedNotificationInfo(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void bindNotification(
+            PackageManager pm,
+            INotificationManager iNotificationManager,
+            OnUserInteractionCallback onUserInteractionCallback,
+            ChannelEditorDialogController channelEditorDialogController,
+            String pkg,
+            NotificationChannel notificationChannel,
+            NotificationEntry entry,
+            OnSettingsClickListener onSettingsClick,
+            OnAppSettingsClickListener onAppSettingsClick,
+            UiEventLogger uiEventLogger,
+            boolean isDeviceProvisioned,
+            boolean isNonblockable,
+            boolean wasShownHighPriority,
+            AssistantFeedbackController assistantFeedbackController,
+            MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException {
+        super.bindNotification(pm, iNotificationManager, onUserInteractionCallback,
+                channelEditorDialogController, pkg, notificationChannel, entry, onSettingsClick,
+                onAppSettingsClick, uiEventLogger, isDeviceProvisioned, isNonblockable,
+                wasShownHighPriority, assistantFeedbackController, metricsLogger, onCloseClick);
+
+        mNotificationManager = iNotificationManager;
+
+        bindDismiss(entry.getSbn(), onCloseClick);
+        bindDemote(entry.getSbn(), pkg);
+    }
+
+
+    protected void bindDismiss(StatusBarNotification sbn,
+            View.OnClickListener onCloseClick) {
+        View dismissButton = findViewById(R.id.promoted_dismiss);
+
+        dismissButton.setOnClickListener(onCloseClick);
+        dismissButton.setVisibility(!sbn.isNonDismissable()
+                && dismissButton.hasOnClickListeners() ? VISIBLE : GONE);
+
+    }
+
+    protected void bindDemote(StatusBarNotification sbn, String packageName) {
+        View demoteButton = findViewById(R.id.promoted_demote);
+        demoteButton.setOnClickListener(getDemoteClickListener(sbn, packageName));
+        demoteButton.setVisibility(demoteButton.hasOnClickListeners() ? VISIBLE : GONE);
+    }
+
+    private OnClickListener getDemoteClickListener(StatusBarNotification sbn, String packageName) {
+        return ((View unusedView) -> {
+            try {
+                // TODO(b/391661009): Signal AutomaticPromotionCoordinator here
+                mNotificationManager.setCanBePromoted(packageName, sbn.getUid(), false, true);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Couldn't revoke live update permission", e);
+            }
+        });
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index fe2803b..c051513 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -61,6 +61,7 @@
         builder: Notification.Builder,
         systemUiContext: Context,
         redactText: Boolean,
+        summarization: String?
     ): SingleLineViewModel {
         if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) {
             return SingleLineViewModel(null, null, null)
@@ -108,6 +109,7 @@
                 conversationSenderName =
                     if (isGroupConversation) conversationTextData?.senderName else null,
                 avatar = conversationAvatar,
+                summarization = summarization
             )
 
         return SingleLineViewModel(
@@ -132,6 +134,7 @@
                                 .ic_redacted_notification_single_line_icon
                         )
                     ),
+                    null
                 )
             } else {
                 null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt
index a17197c..a50fc4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt
@@ -32,6 +32,7 @@
                 viewModel?.titleText,
                 viewModel?.contentText,
                 viewModel?.conversationData?.conversationSenderName,
+                viewModel?.conversationData?.summarization
             )
         } else {
             // bind the title and content text views
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt
index d583fa5..32ded25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt
@@ -46,6 +46,7 @@
 data class ConversationData(
     val conversationSenderName: CharSequence?,
     val avatar: ConversationAvatar,
+    val summarization: String?
 )
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationBundleUi.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationBundleUi.kt
new file mode 100644
index 0000000..7237231
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationBundleUi.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2025 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.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the notification bundle ui flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationBundleUi {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_NOTIFICATION_BUNDLE_UI
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.notificationBundleUi()
+
+    /**
+     * 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 enabled. This will throw an exception if
+     * the flag is not enabled to ensure that the refactor author catches issues in testing.
+     * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+     */
+    @JvmStatic
+    inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(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)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
new file mode 100644
index 0000000..02336e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2025 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.notification.stack
+
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/**
+ * An interface to coordinate the magnetic behavior of notifications when swiped.
+ *
+ * During swiping a notification, this manager receives calls to set the horizontal translation of
+ * the notification and events that indicate that the interaction with the notification ended ( see
+ * the documentation for [onMagneticInteractionEnd]). The latter represent events when the
+ * notification is swiped out by dragging or flinging, or when it snaps back when the view is
+ * released from the gesture.
+ *
+ * This manager uses all of these inputs to implement a magnetic attachment between the notification
+ * swiped and its neighbors, as well as a detaching moment after crossing a threshold.
+ */
+interface MagneticNotificationRowManager {
+
+    /**
+     * Set the swipe threshold in pixels. After crossing the threshold, the magnetic target detaches
+     * and the magnetic neighbors snap back.
+     *
+     * @param[threshold] Swipe threshold in pixels.
+     */
+    fun setSwipeThresholdPx(thresholdPx: Float)
+
+    /**
+     * Set the magnetic and roundable targets of a magnetic swipe interaction.
+     *
+     * This method should construct and set a list of [MagneticRowListener] objects and a
+     * [RoundableTargets] object when an [ExpandableNotificationRow] starts to be swiped. The
+     * magnetic targets interact magnetically as the [expandableNotificationRow] is swiped, and the
+     * [RoundableTargets] get rounded when the row detaches from its magnetic couplings.
+     *
+     * This method must be called when the [swipingRow] starts to be swiped. It represents the
+     * beginning of the magnetic swipe.
+     *
+     * @param[swipingRow] The [ExpandableNotificationRow] that is being swiped. This is the main
+     *   magnetic element that pulls its neighbors as it is swiped.
+     * @param[stackScrollLayout] The [NotificationStackScrollLayout] that contains notifications.
+     * @param[sectionsManager] The [NotificationSectionsManager] that helps identify roundable
+     *   targets.
+     */
+    fun setMagneticAndRoundableTargets(
+        swipingRow: ExpandableNotificationRow,
+        stackScrollLayout: NotificationStackScrollLayout,
+        sectionsManager: NotificationSectionsManager,
+    )
+
+    /**
+     * Set the translation of an [ExpandableNotificationRow].
+     *
+     * This method must be called after [setMagneticAndRoundableTargets] has been called and must be
+     * called for each movement on the [row] being that is being swiped.
+     *
+     * @return true if the given [row] is the current magnetic view being swiped by the user, false
+     *   otherwise. If false, no translation is applied and this method has no effect.
+     */
+    fun setMagneticRowTranslation(row: ExpandableNotificationRow, translation: Float): Boolean
+
+    /**
+     * Notifies that the magnetic interactions with the [ExpandableNotificationRow] stopped.
+     *
+     * This occurs if the row stopped being swiped and will snap back, or if it was ultimately
+     * dismissed. This method represents the end of the magnetic interaction and must be called
+     * after calls to [setMagneticRowTranslation].
+     *
+     * @param[row] [ExpandableNotificationRow] that stopped whose interaction stopped.
+     * @param[velocity] Optional velocity at the end of the interaction. Use this to trigger
+     *   animations with a start velocity.
+     */
+    fun onMagneticInteractionEnd(row: ExpandableNotificationRow, velocity: Float? = null)
+
+    /**
+     * Reset any magnetic and roundable targets set, as well as any internal state.
+     *
+     * This method is in charge of proper cleanup by cancelling animations, clearing targets and
+     * resetting any internal state in the implementation. One use case of this method is when
+     * notifications must be cleared in the middle of a magnetic interaction and
+     * [onMagneticInteractionEnd] will not be called from the lifecycle of the user gesture.
+     */
+    fun reset()
+
+    companion object {
+        /** Detaching threshold in dp */
+        const val MAGNETIC_DETACH_THRESHOLD_DP = 56
+
+        /* An empty implementation of a manager */
+        @JvmStatic
+        val Empty: MagneticNotificationRowManager
+            get() =
+                object : MagneticNotificationRowManager {
+                    override fun setSwipeThresholdPx(thresholdPx: Float) {}
+
+                    override fun setMagneticAndRoundableTargets(
+                        swipingRow: ExpandableNotificationRow,
+                        stackScrollLayout: NotificationStackScrollLayout,
+                        sectionsManager: NotificationSectionsManager,
+                    ) {}
+
+                    override fun setMagneticRowTranslation(
+                        row: ExpandableNotificationRow,
+                        translation: Float,
+                    ): Boolean = false
+
+                    override fun onMagneticInteractionEnd(
+                        row: ExpandableNotificationRow,
+                        velocity: Float?,
+                    ) {}
+
+                    override fun reset() {}
+                }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
new file mode 100644
index 0000000..bddff12
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2025 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.notification.stack
+
+import android.os.VibrationAttributes
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationRowLogger
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.InteractionProperties
+import com.google.android.msdl.domain.MSDLPlayer
+import javax.inject.Inject
+import kotlin.math.abs
+import kotlin.math.pow
+
+@SysUISingleton
+class MagneticNotificationRowManagerImpl
+@Inject
+constructor(
+    private val msdlPlayer: MSDLPlayer,
+    private val notificationTargetsHelper: NotificationTargetsHelper,
+    private val notificationRoundnessManager: NotificationRoundnessManager,
+    private val logger: NotificationRowLogger,
+) : MagneticNotificationRowManager {
+
+    var currentState = State.IDLE
+        private set
+
+    // Magnetic and roundable targets
+    var currentMagneticListeners = listOf<MagneticRowListener?>()
+        private set
+
+    var currentRoundableTargets: RoundableTargets? = null
+        private set
+
+    private var magneticDetachThreshold = Float.POSITIVE_INFINITY
+
+    // Animation spring forces
+    private val detachForce =
+        SpringForce().setStiffness(DETACH_STIFFNESS).setDampingRatio(DETACH_DAMPING_RATIO)
+    private val snapForce =
+        SpringForce().setStiffness(SNAP_BACK_STIFFNESS).setDampingRatio(SNAP_BACK_DAMPING_RATIO)
+
+    // Multiplier applied to the translation of a row while swiped
+    private val swipedRowMultiplier =
+        MAGNETIC_TRANSLATION_MULTIPLIERS[MAGNETIC_TRANSLATION_MULTIPLIERS.size / 2]
+
+    override fun setSwipeThresholdPx(thresholdPx: Float) {
+        magneticDetachThreshold = thresholdPx
+    }
+
+    override fun setMagneticAndRoundableTargets(
+        swipingRow: ExpandableNotificationRow,
+        stackScrollLayout: NotificationStackScrollLayout,
+        sectionsManager: NotificationSectionsManager,
+    ) {
+        if (currentState == State.IDLE) {
+            updateMagneticAndRoundableTargets(swipingRow, stackScrollLayout, sectionsManager)
+            currentState = State.TARGETS_SET
+        } else {
+            logger.logMagneticAndRoundableTargetsNotSet(currentState, swipingRow.entry)
+        }
+    }
+
+    private fun updateMagneticAndRoundableTargets(
+        expandableNotificationRow: ExpandableNotificationRow,
+        stackScrollLayout: NotificationStackScrollLayout,
+        sectionsManager: NotificationSectionsManager,
+    ) {
+        // Update roundable targets
+        currentRoundableTargets =
+            notificationTargetsHelper.findRoundableTargets(
+                expandableNotificationRow,
+                stackScrollLayout,
+                sectionsManager,
+            )
+
+        // Update magnetic targets
+        val newListeners =
+            notificationTargetsHelper.findMagneticTargets(
+                expandableNotificationRow,
+                stackScrollLayout,
+                MAGNETIC_TRANSLATION_MULTIPLIERS.size,
+            )
+        newListeners.forEach {
+            if (currentMagneticListeners.contains(it)) {
+                it?.cancelMagneticAnimations()
+            }
+        }
+        currentMagneticListeners = newListeners
+    }
+
+    override fun setMagneticRowTranslation(
+        row: ExpandableNotificationRow,
+        translation: Float,
+    ): Boolean {
+        if (!row.isSwipedTarget()) return false
+
+        when (currentState) {
+            State.TARGETS_SET -> {
+                pullTargets(translation)
+                currentState = State.PULLING
+            }
+            State.PULLING -> {
+                val targetTranslation = swipedRowMultiplier * translation
+                val crossedThreshold = abs(targetTranslation) >= magneticDetachThreshold
+                if (crossedThreshold) {
+                    snapNeighborsBack()
+                    currentMagneticListeners.swipedListener()?.let { detach(it, translation) }
+                    currentState = State.DETACHED
+                } else {
+                    pullTargets(translation)
+                }
+            }
+            State.DETACHED -> {
+                val swiped = currentMagneticListeners.swipedListener()
+                swiped?.setMagneticTranslation(translation)
+            }
+            else -> {}
+        }
+        return true
+    }
+
+    private fun pullTargets(translation: Float) {
+        var targetTranslation: Float
+        currentMagneticListeners.forEachIndexed { i, listener ->
+            targetTranslation = MAGNETIC_TRANSLATION_MULTIPLIERS[i] * translation
+            listener?.setMagneticTranslation(targetTranslation)
+        }
+        playPullHaptics(mappedTranslation = swipedRowMultiplier * translation)
+    }
+
+    private fun playPullHaptics(mappedTranslation: Float) {
+        val normalizedTranslation = abs(mappedTranslation) / magneticDetachThreshold
+        val vibrationScale =
+            (normalizedTranslation * MAX_VIBRATION_SCALE).pow(VIBRATION_PERCEPTION_EXPONENT)
+        msdlPlayer.playToken(
+            MSDLToken.DRAG_INDICATOR_CONTINUOUS,
+            InteractionProperties.DynamicVibrationScale(
+                scale = vibrationScale,
+                vibrationAttributes = VIBRATION_ATTRIBUTES_PIPELINING,
+            ),
+        )
+    }
+
+    private fun snapNeighborsBack(velocity: Float? = null) {
+        currentMagneticListeners.forEachIndexed { i, target ->
+            target?.let {
+                if (i != currentMagneticListeners.size / 2) {
+                    snapBack(it, velocity)
+                }
+            }
+        }
+    }
+
+    private fun detach(listener: MagneticRowListener, toPosition: Float) {
+        listener.cancelMagneticAnimations()
+        listener.triggerMagneticForce(toPosition, detachForce)
+        currentRoundableTargets?.let {
+            notificationRoundnessManager.setViewsAffectedBySwipe(it.before, it.swiped, it.after)
+        }
+        msdlPlayer.playToken(MSDLToken.SWIPE_THRESHOLD_INDICATOR)
+    }
+
+    private fun snapBack(listener: MagneticRowListener, velocity: Float?) {
+        listener.cancelMagneticAnimations()
+        listener.triggerMagneticForce(
+            endTranslation = 0f,
+            snapForce,
+            startVelocity = velocity ?: 0f,
+        )
+    }
+
+    override fun onMagneticInteractionEnd(row: ExpandableNotificationRow, velocity: Float?) {
+        if (!row.isSwipedTarget()) return
+
+        when (currentState) {
+            State.PULLING -> {
+                snapNeighborsBack(velocity)
+                currentState = State.IDLE
+            }
+            State.DETACHED -> {
+                currentState = State.IDLE
+            }
+            else -> {}
+        }
+    }
+
+    override fun reset() {
+        currentMagneticListeners.forEach { it?.cancelMagneticAnimations() }
+        currentState = State.IDLE
+        currentMagneticListeners = listOf()
+        currentRoundableTargets = null
+    }
+
+    private fun List<MagneticRowListener?>.swipedListener(): MagneticRowListener? =
+        getOrNull(index = size / 2)
+
+    private fun ExpandableNotificationRow.isSwipedTarget(): Boolean =
+        magneticRowListener == currentMagneticListeners.swipedListener()
+
+    enum class State {
+        IDLE,
+        TARGETS_SET,
+        PULLING,
+        DETACHED,
+    }
+
+    companion object {
+        /**
+         * Multipliers applied to the translation of magnetically-coupled views. This list must be
+         * symmetric with an odd size, where the center multiplier applies to the view that is
+         * currently being swiped. From the center outwards, the multipliers apply to the neighbors
+         * of the swiped view.
+         */
+        private val MAGNETIC_TRANSLATION_MULTIPLIERS = listOf(0.18f, 0.28f, 0.5f, 0.28f, 0.18f)
+
+        /** Spring parameters for physics animators */
+        private const val DETACH_STIFFNESS = 800f
+        private const val DETACH_DAMPING_RATIO = 0.95f
+        private const val SNAP_BACK_STIFFNESS = 550f
+        private const val SNAP_BACK_DAMPING_RATIO = 0.52f
+
+        private val VIBRATION_ATTRIBUTES_PIPELINING =
+            VibrationAttributes.Builder()
+                .setUsage(VibrationAttributes.USAGE_TOUCH)
+                .setFlags(VibrationAttributes.FLAG_PIPELINED_EFFECT)
+                .build()
+        private const val MAX_VIBRATION_SCALE = 0.2f
+        private const val VIBRATION_PERCEPTION_EXPONENT = 1 / 0.89f
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt
new file mode 100644
index 0000000..8a1adfe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 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.notification.stack
+
+import androidx.dynamicanimation.animation.SpringForce
+
+/** A listener that responds to magnetic forces applied to an [ExpandableNotificationRow] */
+interface MagneticRowListener {
+
+    /** Set a translation due to a magnetic attachment. */
+    fun setMagneticTranslation(translation: Float)
+
+    /**
+     * Trigger the magnetic behavior when the row detaches or snaps back from its magnetic
+     * couplings.
+     *
+     * @param[endTranslation] Translation that the row detaches to.
+     * @param[springForce] A [SpringForce] that guides the dynamics of the behavior towards the end
+     *   translation. This could be a detachment spring force or a snap-back spring force.
+     * @param[startVelocity] A start velocity for the animation.
+     */
+    fun triggerMagneticForce(
+        endTranslation: Float,
+        springForce: SpringForce,
+        startVelocity: Float = 0f,
+    )
+
+    /** Cancel any animations related to the magnetic interactions of the row */
+    fun cancelMagneticAnimations()
+}
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 8e48065..ea397b6 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
@@ -77,7 +77,7 @@
     static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
     public static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
     private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
-        private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
+        private final AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
 
         @Override
         public AnimationFilter getAnimationFilter() {
@@ -123,6 +123,8 @@
     private NotificationHeaderViewWrapper mMinimizedGroupHeaderWrapper;
     private NotificationGroupingUtil mGroupingUtil;
     private ViewState mHeaderViewState;
+    private ViewState mTopLineViewState;
+    private ViewState mExpandButtonViewState;
     private int mClipBottomAmount;
     private boolean mIsMinimized;
     private OnClickListener mHeaderClickListener;
@@ -138,7 +140,7 @@
     private float mHeaderVisibleAmount = 1.0f;
     private int mUntruncatedChildCount;
     private boolean mContainingNotificationIsFaded = false;
-    private RoundableState mRoundableState;
+    private final RoundableState mRoundableState;
     private int mMinSingleLineHeight;
 
     private NotificationChildrenContainerLogger mLogger;
@@ -446,7 +448,7 @@
         }
         mGroupHeaderWrapper.setExpanded(mChildrenExpanded);
         mGroupHeaderWrapper.onContentUpdated(mContainingNotification);
-        recreateLowPriorityHeader(builder, isConversation);
+        recreateLowPriorityHeader(builder);
         updateHeaderVisibility(false /* animate */);
         updateChildrenAppearance();
         Trace.endSection();
@@ -559,7 +561,7 @@
      * @param builder a builder to reuse. Otherwise the builder will be recovered.
      */
     @VisibleForTesting
-    void recreateLowPriorityHeader(Notification.Builder builder, boolean isConversation) {
+    void recreateLowPriorityHeader(Notification.Builder builder) {
         AsyncGroupHeaderViewInflation.assertInLegacyMode();
         RemoteViews header;
         StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
@@ -866,10 +868,7 @@
             }
         }
         if (mGroupHeader != null) {
-            if (mHeaderViewState == null) {
-                mHeaderViewState = new ViewState();
-            }
-            mHeaderViewState.initFrom(mGroupHeader);
+            mHeaderViewState = initStateForGroupHeader(mHeaderViewState);
 
             if (mContainingNotification.hasExpandingChild()) {
                 // Not modifying translationZ during expand animation.
@@ -881,38 +880,33 @@
             }
             mHeaderViewState.setYTranslation(mCurrentHeaderTranslation);
             mHeaderViewState.setAlpha(mHeaderVisibleAmount);
-            // The hiding is done automatically by the alpha, otherwise we'll pick it up again
-            // in the next frame with the initFrom call above and have an invisible header
-            mHeaderViewState.hidden = false;
+
+            if (notificationsRedesignTemplates()) {
+                mTopLineViewState = initStateForGroupHeader(mTopLineViewState);
+                mTopLineViewState.setYTranslation(
+                        mGroupHeader.getTopLineTranslation() * expandFactor);
+
+                mExpandButtonViewState = initStateForGroupHeader(mExpandButtonViewState);
+                mExpandButtonViewState.setYTranslation(
+                        mGroupHeader.getExpandButtonTranslation() * expandFactor);
+            }
         }
     }
 
     /**
-     * When moving into the bottom stack, the bottom visible child in an expanded group adjusts its
-     * height, children in the group after this are gone.
-     *
-     * @param child        the child who's height to adjust.
-     * @param parentHeight the height of the parent.
-     * @param childState   the state to update.
-     * @param yPosition    the yPosition of the view.
-     * @return true if children after this one should be hidden.
+     * Initialise a new ViewState for the group header or its children, or update and return
+     * {@code existingState} if not null.
      */
-    private boolean updateChildStateForExpandedGroup(
-            ExpandableNotificationRow child,
-            int parentHeight,
-            ExpandableViewState childState,
-            int yPosition) {
-        final int top = yPosition + child.getClipTopAmount();
-        final int intrinsicHeight = child.getIntrinsicHeight();
-        final int bottom = top + intrinsicHeight;
-        int newHeight = intrinsicHeight;
-        if (bottom >= parentHeight) {
-            // Child is either clipped or gone
-            newHeight = Math.max((parentHeight - top), 0);
+    private ViewState initStateForGroupHeader(ViewState existingState) {
+        ViewState viewState = existingState;
+        if (viewState == null) {
+            viewState = new ViewState();
         }
-        childState.hidden = newHeight == 0;
-        childState.height = newHeight;
-        return childState.height != intrinsicHeight && !childState.hidden;
+        viewState.initFrom(mGroupHeader);
+        // The hiding is done automatically by the alpha, otherwise we'll pick it up again
+        // in the next frame with the initFrom call above and have an invisible header
+        viewState.hidden = false;
+        return viewState;
     }
 
     @VisibleForTesting
@@ -976,6 +970,14 @@
         if (mHeaderViewState != null) {
             mHeaderViewState.applyToView(mGroupHeader);
         }
+        if (notificationsRedesignTemplates()) {
+            if (mTopLineViewState != null) {
+                mTopLineViewState.applyToView(mGroupHeader.getTopLineView());
+            }
+            if (mExpandButtonViewState != null) {
+                mExpandButtonViewState.applyToView(mGroupHeader.getExpandButton());
+            }
+        }
         updateChildrenClipping();
     }
 
@@ -1010,7 +1012,7 @@
     }
 
     @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+    protected boolean drawChild(@NonNull Canvas canvas, View child, long drawingTime) {
         boolean isCanvasChanged = false;
 
         Path clipPath = mChildClipPath;
@@ -1062,16 +1064,6 @@
         }
     }
 
-
-    /**
-     * This is called when the children expansion has changed and positions the children properly
-     * for an appear animation.
-     */
-    public void prepareExpansionChanged() {
-        // TODO: do something that makes sense, like placing the invisible views correctly
-        return;
-    }
-
     /**
      * Animate to a given state.
      */
@@ -1478,7 +1470,7 @@
         return mIsMinimized && !mContainingNotification.isExpanded();
     }
 
-    public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
+    public void reInflateViews(OnClickListener listener) {
         if (!AsyncGroupHeaderViewInflation.isEnabled()) {
             // When Async header inflation is enabled, we do not reinflate headers because they are
             // inflated from the background thread
@@ -1567,7 +1559,7 @@
         mIsMinimized = isMinimized;
         if (mContainingNotification != null) { /* we're not yet set up yet otherwise */
             if (!AsyncGroupHeaderViewInflation.isEnabled()) {
-                recreateLowPriorityHeader(null /* existingBuilder */, mIsConversation);
+                recreateLowPriorityHeader(null /* existingBuilder */);
             }
             updateHeaderVisibility(false /* animate */);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index bf24c87..8760901 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -24,6 +24,7 @@
 import static com.android.app.tracing.TrackGroupUtils.trackGroup;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
+import static com.android.systemui.Flags.magneticNotificationSwipes;
 import static com.android.systemui.Flags.notificationOverExpansionClippingFix;
 import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
@@ -5787,17 +5788,21 @@
                 getChildrenWithBackground()
         );
 
-        RoundableTargets targets = mController.getNotificationTargetsHelper().findRoundableTargets(
-                (ExpandableNotificationRow) viewSwiped,
-                this,
-                mSectionsManager
-        );
+        if (!magneticNotificationSwipes()) {
+            RoundableTargets targets = mController
+                    .getNotificationTargetsHelper()
+                    .findRoundableTargets(
+                            (ExpandableNotificationRow) viewSwiped,
+                            this,
+                            mSectionsManager);
 
-        mController.getNotificationRoundnessManager()
-                .setViewsAffectedBySwipe(
-                        targets.getBefore(),
-                        targets.getSwiped(),
-                        targets.getAfter());
+            mController.getNotificationRoundnessManager()
+                    .setViewsAffectedBySwipe(
+                            targets.getBefore(),
+                            targets.getSwiped(),
+                            targets.getAfter());
+
+        }
 
         updateFirstAndLastBackgroundViews();
         requestDisallowInterceptTouchEvent(true);
@@ -6678,10 +6683,23 @@
         }
         NotificationHeaderView header = childrenContainer.getGroupHeader();
         if (header != null) {
+            resetYTranslation(header.getTopLineView());
+            resetYTranslation(header.getExpandButton());
             header.centerTopLine(expanded);
         }
     }
 
+    /**
+     * Reset the y translation of the {@code view} via the {@link ViewState}, to ensure that the
+     * animation state is updated correctly.
+     */
+    private static void resetYTranslation(View view) {
+        ViewState viewState = new ViewState();
+        viewState.initFrom(view);
+        viewState.setYTranslation(0);
+        viewState.applyToView(view);
+    }
+
     private final ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
         @Override
         public ExpandableView getChildAtPosition(float touchX, float touchY) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 8eaef36..8048245 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -188,6 +188,8 @@
     private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
     private final StackStateLogger mStackStateLogger;
     private final NotificationStackScrollLogger mLogger;
+    private final MagneticNotificationRowManager mMagneticNotificationRowManager;
+    private final NotificationSectionsManager mSectionsManager;
 
     private final GroupExpansionManager mGroupExpansionManager;
     private NotificationStackScrollLayout mView;
@@ -465,6 +467,28 @@
                 }
 
                 @Override
+                public void onDensityScaleChange(float density) {
+                    mMagneticNotificationRowManager.setSwipeThresholdPx(
+                            density * MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP
+                    );
+                }
+
+                @Override
+                public boolean handleSwipeableViewTranslation(SwipeableView view, float translate) {
+                    if (view instanceof ExpandableNotificationRow row) {
+                        return mMagneticNotificationRowManager
+                                .setMagneticRowTranslation(row, translate);
+                    } else {
+                        return false;
+                    }
+                }
+
+                @Override
+                public void resetMagneticStates() {
+                    mMagneticNotificationRowManager.reset();
+                }
+
+                @Override
                 public void onSnooze(StatusBarNotification sbn,
                         NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
                     mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
@@ -479,6 +503,14 @@
                 public void onDragCancelled(View v) {
                 }
 
+                @Override
+                public void onDragCancelledWithVelocity(View v, float finalVelocity) {
+                    if (v instanceof ExpandableNotificationRow row) {
+                        mMagneticNotificationRowManager.onMagneticInteractionEnd(
+                                row, finalVelocity);
+                    }
+                }
+
                 /**
                  * Handles cleanup after the given {@code view} has been fully swiped out (including
                  * re-invoking dismiss logic in case the notification has not made its way out yet).
@@ -506,6 +538,10 @@
                  */
 
                 public void handleChildViewDismissed(View view) {
+                    if (view instanceof ExpandableNotificationRow row) {
+                        mMagneticNotificationRowManager.onMagneticInteractionEnd(
+                                row, null /* velocity */);
+                    }
                     // The View needs to clean up the Swipe states, e.g. roundness.
                     mView.onSwipeEnd();
                     if (mView.getClearAllInProgress()) {
@@ -577,6 +613,10 @@
 
                 @Override
                 public void onBeginDrag(View v) {
+                    if (v instanceof ExpandableNotificationRow row) {
+                        mMagneticNotificationRowManager.setMagneticAndRoundableTargets(
+                                row, mView, mSectionsManager);
+                    }
                     mView.onSwipeBegin(v);
                 }
 
@@ -691,7 +731,9 @@
             ActivityStarter activityStarter,
             SplitShadeStateController splitShadeStateController,
             SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
-            WallpaperInteractor wallpaperInteractor) {
+            WallpaperInteractor wallpaperInteractor,
+            MagneticNotificationRowManager magneticNotificationRowManager,
+            NotificationSectionsManager sectionsManager) {
         mView = view;
         mViewBinder = viewBinder;
         mStackStateLogger = stackLogger;
@@ -742,6 +784,8 @@
         mSensitiveNotificationProtectionController = sensitiveNotificationProtectionController;
         mWallpaperInteractor = wallpaperInteractor;
         mView.passSplitShadeStateController(splitShadeStateController);
+        mMagneticNotificationRowManager = magneticNotificationRowManager;
+        mSectionsManager = sectionsManager;
         if (SceneContainerFlag.isEnabled()) {
             mWakeUpCoordinator.setStackScroller(this);
         }
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 a96d972..08bc8f5 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
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.StatusBarState.KEYGUARD
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
@@ -463,7 +464,12 @@
 
         var size =
             if (onLockscreen) {
-                if (view is ExpandableNotificationRow && view.entry.isStickyAndNotDemoted) {
+                if (
+                    view is ExpandableNotificationRow &&
+                        (view.entry.isStickyAndNotDemoted ||
+                            (PromotedNotificationUiForceExpanded.isEnabled &&
+                                view.isPromotedOngoing))
+                ) {
                     height
                 } else {
                     view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 5045744..d476d48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -34,7 +34,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.systemui.SwipeHelper;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.FalsingManager;
@@ -363,7 +362,7 @@
             superSnapChild(animView, targetLeft, velocity);
         }
 
-        mCallback.onDragCancelled(animView);
+        mCallback.onDragCancelledWithVelocity(animView, velocity);
         if (targetLeft == 0) {
             handleMenuCoveredOrDismissed();
         }
@@ -404,7 +403,11 @@
     @Override
     public void setTranslation(View v, float translate) {
         if (v instanceof SwipeableView) {
-            ((SwipeableView) v).setTranslation(translate);
+            boolean setTranslationHandled =
+                    mCallback.handleSwipeableViewTranslation((SwipeableView) v, translate);
+            if (!setTranslationHandled) {
+                ((SwipeableView) v).setTranslation(translate);
+            }
         }
     }
 
@@ -529,6 +532,18 @@
         mPulsing = pulsing;
     }
 
+    @Override
+    public void setDensityScale(float densityScale) {
+        super.setDensityScale(densityScale);
+        mCallback.onDensityScaleChange(densityScale);
+    }
+
+    @Override
+    public void resetTouchState() {
+        super.resetTouchState();
+        mCallback.resetMagneticStates();
+    }
+
     public interface NotificationCallback extends SwipeHelper.Callback{
         /**
          * @return if the view should be dismissed as soon as the touch is released, otherwise its
@@ -548,6 +563,13 @@
          * @param animView the view to ask about
          */
         float getTotalTranslationLength(View animView);
+
+        void onDensityScaleChange(float density);
+
+        boolean handleSwipeableViewTranslation(SwipeableView view, float translate);
+
+        // Reset any ongoing magnetic interactions
+        void resetMagneticStates();
     }
 
     static class Builder {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt
index 02662f4..74e8b8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt
@@ -3,7 +3,6 @@
 import androidx.core.view.children
 import androidx.core.view.isVisible
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.notification.Roundable
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -14,11 +13,7 @@
  * ([Roundable]) above and below the current one (see [findRoundableTargets]).
  */
 @SysUISingleton
-class NotificationTargetsHelper
-@Inject
-constructor(
-    featureFlags: FeatureFlags,
-) {
+class NotificationTargetsHelper @Inject constructor() {
 
     /**
      * This method looks for views that can be rounded (and implement [Roundable]) during a
@@ -74,11 +69,80 @@
                 }
         }
 
-        return RoundableTargets(
-            before = viewBefore,
-            swiped = viewSwiped,
-            after = viewAfter,
-        )
+        return RoundableTargets(before = viewBefore, swiped = viewSwiped, after = viewAfter)
+    }
+
+    /**
+     * This method looks for [ExpandableNotificationRow]s that can magnetically attach to a swiped
+     * [ExpandableNotificationRow] and returns their [MagneticRowListener]s in a list.
+     *
+     * The list contains the swiped row's listener at the center of the list. From the center
+     * towards the left, the list contains the closest notification neighbors above the swiped row.
+     * From the center towards the right, the list contains the closest neighbors below the row.
+     *
+     * The list is filled from the center outwards, stopping at the first neighbor that is not an
+     * [ExpandableNotificationRow]. In addition, the list does not cross the boundaries of a
+     * notification group. Positions where the list halted are filled with null.
+     *
+     * @param[viewSwiped] The [ExpandableNotificationRow] that is swiped.
+     * @param[stackScrollLayout] [NotificationStackScrollLayout] container.
+     * @param[totalMagneticTargets] The total number of magnetic listeners in the resulting list.
+     *   This includes the listener of the view swiped.
+     * @return The list of [MagneticRowListener]s above and below the swiped
+     *   [ExpandableNotificationRow]
+     */
+    fun findMagneticTargets(
+        viewSwiped: ExpandableNotificationRow,
+        stackScrollLayout: NotificationStackScrollLayout,
+        totalMagneticTargets: Int,
+    ): List<MagneticRowListener?> {
+        val notificationParent = viewSwiped.notificationParent
+        val childrenContainer = notificationParent?.childrenContainer
+        val visibleStackChildren =
+            stackScrollLayout.children
+                .filterIsInstance<ExpandableView>()
+                .filter { it.isVisible }
+                .toList()
+
+        val container: List<ExpandableView> =
+            if (notificationParent != null && childrenContainer != null) {
+                // We are inside a notification group
+                childrenContainer.attachedChildren.filter { it.isVisible }
+            } else {
+                visibleStackChildren
+            }
+
+        // Construct the list of targets
+        val magneticTargets = MutableList<MagneticRowListener?>(totalMagneticTargets) { null }
+        magneticTargets[totalMagneticTargets / 2] = viewSwiped.magneticRowListener
+
+        // Fill the list outwards from the center
+        val centerIndex = container.indexOf(viewSwiped)
+        var leftIndex = magneticTargets.size / 2 - 1
+        var rightIndex = magneticTargets.size / 2 + 1
+        var canMoveLeft = true
+        var canMoveRight = true
+        for (distance in 1..totalMagneticTargets / 2) {
+            if (canMoveLeft) {
+                val leftElement = container.getOrNull(index = centerIndex - distance)
+                if (leftElement is ExpandableNotificationRow) {
+                    magneticTargets[leftIndex] = leftElement.magneticRowListener
+                    leftIndex--
+                } else {
+                    canMoveLeft = false
+                }
+            }
+            if (canMoveRight) {
+                val rightElement = container.getOrNull(index = centerIndex + distance)
+                if (rightElement is ExpandableNotificationRow) {
+                    magneticTargets[rightIndex] = rightElement.magneticRowListener
+                    rightIndex++
+                } else {
+                    canMoveRight = false
+                }
+            }
+        }
+        return magneticTargets
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 4686bef..c783250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -468,10 +468,6 @@
                 if (isFullySwipedOut) {
                     changingView.removeFromTransientContainer();
                 }
-            } else if (event.animationType == NotificationStackScrollLayout
-                    .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) event.mChangingView;
-                row.prepareExpansionChanged();
             } else if (event.animationType == ANIMATION_TYPE_HEADS_UP_CYCLING_IN) {
                 mHeadsUpAppearChildren.add(changingView);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
index 53d0c2e..1904bc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.race
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -41,7 +40,6 @@
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.withTimeout
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class HideNotificationsInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index b6ce708..107875d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import dagger.Lazy
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -40,7 +39,7 @@
 import kotlinx.coroutines.flow.map
 
 /** Encapsulates business-logic specifically related to the shared notification stack container. */
-@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
+@OptIn(FlowPreview::class)
 @SysUISingleton
 class SharedNotificationContainerInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 1965b95..f740144 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -36,6 +36,8 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
 
 /** Binds the shared notification container to its view-model. */
 @SysUISingleton
@@ -143,21 +145,25 @@
                     if (!SceneContainerFlag.isEnabled) {
                         if (Flags.magicPortraitWallpapers()) {
                             launch {
-                                viewModel
-                                    .getNotificationStackAbsoluteBottom(
-                                        calculateMaxNotifications = calculateMaxNotifications,
-                                        calculateHeight = { maxNotifications ->
-                                            notificationStackSizeCalculator.computeHeight(
-                                                maxNotifs = maxNotifications,
-                                                shelfHeight = controller.getShelfHeight().toFloat(),
-                                                stack = controller.view,
-                                            )
-                                        },
-                                        controller.getShelfHeight().toFloat(),
+                                combine(
+                                        viewModel.getNotificationStackAbsoluteBottom(
+                                            calculateMaxNotifications = calculateMaxNotifications,
+                                            calculateHeight = { maxNotifications ->
+                                                notificationStackSizeCalculator.computeHeight(
+                                                    maxNotifs = maxNotifications,
+                                                    shelfHeight =
+                                                        controller.getShelfHeight().toFloat(),
+                                                    stack = controller.view,
+                                                )
+                                            },
+                                            controller.getShelfHeight().toFloat(),
+                                        ),
+                                        viewModel.configurationBasedDimensions.map { it.marginTop },
+                                        ::Pair,
                                     )
-                                    .collect { bottom ->
+                                    .collect { (bottom: Float, marginTop: Int) ->
                                         keyguardInteractor.setNotificationStackAbsoluteBottom(
-                                            bottom
+                                            marginTop + bottom
                                         )
                                     }
                             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index c1d0226..b6d89ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -15,8 +15,6 @@
  *
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
 import android.content.Context
@@ -92,7 +90,6 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index 33e4fed..7289c2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -66,14 +66,12 @@
 import dagger.Lazy
 import java.util.Optional
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /**
  * Encapsulates the activity logic for activity starter when the SceneContainerFlag is enabled.
  *
  * TODO: b/308819693
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class ActivityStarterInternalImpl
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index afc5bc6..04dedb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -71,8 +71,6 @@
 
 import dagger.Lazy;
 
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
-
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -86,7 +84,6 @@
 /**
  * Controller which coordinates all the biometric unlocking actions with the UI.
  */
-@ExperimentalCoroutinesApi
 @SysUISingleton
 public class BiometricUnlockController extends KeyguardUpdateMonitorCallback implements Dumpable {
     private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 6922de5..09531c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -64,14 +64,12 @@
 
 import kotlin.Unit;
 
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
-
 import javax.inject.Inject;
 
 /**
  * Implementation of DozeHost for SystemUI.
  */
-@ExperimentalCoroutinesApi @SysUISingleton
+@SysUISingleton
 public final class DozeServiceHost implements DozeHost {
     private static final String TAG = "DozeServiceHost";
     private final IListenerSet<Callback> mCallbacks = new CopyOnLoopListenerSet<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 16e9c71..a2f1ded 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -26,24 +26,31 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.TextView;
 
 import androidx.annotation.StyleRes;
+import androidx.core.graphics.ColorUtils;
 
 import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Flags;
 import com.android.systemui.keyguard.KeyguardIndication;
 import com.android.systemui.res.R;
+import com.android.systemui.shared.shadow.DoubleShadowTextView;
 
 /**
  * A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
  */
-public class KeyguardIndicationTextView extends TextView {
+public class KeyguardIndicationTextView extends DoubleShadowTextView {
+    // Minimum luminance for texts to receive shadows.
+    private static final float MIN_TEXT_SHADOW_LUMINANCE = 0.5f;
     public static final long Y_IN_DURATION = 600L;
 
     @StyleRes
     private static int sStyleId = R.style.TextAppearance_Keyguard_BottomArea;
     @StyleRes
+    private static int sStyleWithDoubleShadowTextId =
+            R.style.TextAppearance_Keyguard_BottomArea_DoubleShadow;
+    @StyleRes
     private static int sButtonStyleId = R.style.TextAppearance_Keyguard_BottomArea_Button;
 
     private boolean mAnimationsEnabled = true;
@@ -226,7 +233,14 @@
             if (mKeyguardIndicationInfo.getBackground() != null) {
                 setTextAppearance(sButtonStyleId);
             } else {
-                setTextAppearance(sStyleId);
+                // If text is transparent or dark color, don't draw any shadow
+                if (Flags.indicationTextA11yFix() && ColorUtils.calculateLuminance(
+                        mKeyguardIndicationInfo.getTextColor().getDefaultColor())
+                        > MIN_TEXT_SHADOW_LUMINANCE) {
+                    setTextAppearance(sStyleWithDoubleShadowTextId);
+                } else {
+                    setTextAppearance(sStyleId);
+                }
             }
             setBackground(mKeyguardIndicationInfo.getBackground());
             setTextColor(mKeyguardIndicationInfo.getTextColor());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 4c2bfe5..40245ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -23,6 +23,7 @@
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
+import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -56,6 +57,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.shade.ShadeDisplayAware;
 import com.android.systemui.shade.ShadeViewStateProvider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusBarState;
@@ -114,6 +116,7 @@
             R.id.keyguard_hun_animator_start_tag);
 
     private final CoroutineDispatcher mCoroutineDispatcher;
+    private final Context mContext;
     private final CarrierTextController mCarrierTextController;
     private final ConfigurationController mConfigurationController;
     private final SystemStatusAnimationScheduler mAnimationScheduler;
@@ -129,7 +132,7 @@
     private final KeyguardStatusBarViewModel mKeyguardStatusBarViewModel;
     private final BiometricUnlockController mBiometricUnlockController;
     private final SysuiStatusBarStateController mStatusBarStateController;
-    private final StatusBarContentInsetsProvider mInsetsProvider;
+    private final StatusBarContentInsetsProviderStore mInsetsProviderStore;
     private final UserManager mUserManager;
     private final StatusBarUserChipViewModel mStatusBarUserChipViewModel;
     private final SecureSettings mSecureSettings;
@@ -314,6 +317,7 @@
     @Inject
     public KeyguardStatusBarViewController(
             @Main CoroutineDispatcher dispatcher,
+            @ShadeDisplayAware Context context,
             KeyguardStatusBarView view,
             CarrierTextController carrierTextController,
             ConfigurationController configurationController,
@@ -347,6 +351,7 @@
     ) {
         super(view);
         mCoroutineDispatcher = dispatcher;
+        mContext = context;
         mCarrierTextController = carrierTextController;
         mConfigurationController = configurationController;
         mAnimationScheduler = animationScheduler;
@@ -362,7 +367,7 @@
         mKeyguardStatusBarViewModel = keyguardStatusBarViewModel;
         mBiometricUnlockController = biometricUnlockController;
         mStatusBarStateController = statusBarStateController;
-        mInsetsProvider = statusBarContentInsetsProviderStore.getDefaultDisplay();
+        mInsetsProviderStore = statusBarContentInsetsProviderStore;
         mUserManager = userManager;
         mStatusBarUserChipViewModel = userChipViewModel;
         mSecureSettings = secureSettings;
@@ -404,6 +409,10 @@
         mStatusOverlayHoverListenerFactory = statusOverlayHoverListenerFactory;
     }
 
+    private StatusBarContentInsetsProvider insetsProvider() {
+        return mInsetsProviderStore.forDisplay(mContext.getDisplayId());
+    }
+
     @Override
     protected void onInit() {
         super.onInit();
@@ -446,7 +455,7 @@
                 .createDarkAwareListener(mSystemIconsContainer, mView.darkChangeFlow());
         mSystemIconsContainer.setOnHoverListener(hoverListener);
         mView.setOnApplyWindowInsetsListener(
-                (view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider));
+                (view, windowInsets) -> mView.updateWindowInsets(windowInsets, insetsProvider()));
         mSecureSettings.registerContentObserverForUserSync(
                 Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
                 false,
@@ -645,7 +654,7 @@
      * {@code OnApplyWindowInsetsListener}s.
      */
     public void setDisplayCutout(@Nullable DisplayCutout displayCutout) {
-        mView.setDisplayCutout(displayCutout, mInsetsProvider);
+        mView.setDisplayCutout(displayCutout, insetsProvider());
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 3f44f7b..caf8a43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -46,7 +46,6 @@
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
-import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.policy.Clock
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
@@ -84,7 +83,7 @@
     private val configurationController: ConfigurationController,
     private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
     private val darkIconDispatcher: DarkIconDispatcher,
-    private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
+    private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
     private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
 ) : ViewController<PhoneStatusBarView>(view) {
 
@@ -92,6 +91,8 @@
     private lateinit var clock: Clock
     private lateinit var startSideContainer: View
     private lateinit var endSideContainer: View
+    private val statusBarContentInsetsProvider
+        get() = statusBarContentInsetsProviderStore.forDisplay(context.displayId)
 
     private val iconsOnTouchListener =
         object : View.OnTouchListener {
@@ -189,11 +190,9 @@
     init {
         // These should likely be done in `onInit`, not `init`.
         mView.setTouchEventHandler(PhoneStatusBarViewTouchHandler())
-        mView.setHasCornerCutoutFetcher {
-            statusBarContentInsetsProvider.currentRotationHasCornerCutout()
-        }
-        mView.setInsetsFetcher {
-            statusBarContentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
+        statusBarContentInsetsProvider?.let {
+            mView.setHasCornerCutoutFetcher { it.currentRotationHasCornerCutout() }
+            mView.setInsetsFetcher { it.getStatusBarContentInsetsForCurrentRotation() }
         }
         mView.init(userChipViewModel)
     }
@@ -393,7 +392,7 @@
                 configurationController,
                 statusOverlayHoverListenerFactory,
                 darkIconDispatcher,
-                statusBarContentInsetsProviderStore.defaultDisplay,
+                statusBarContentInsetsProviderStore,
                 lazyStatusBarShadeDisplayPolicy,
             )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 0f6c306..d5f456f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -66,6 +66,7 @@
 import com.android.systemui.keyguard.shared.model.ScrimAlpha;
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.transitions.BlurConfig;
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.res.R;
@@ -83,7 +84,6 @@
 import com.android.systemui.util.wakelock.WakeLock;
 
 import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -98,7 +98,6 @@
  * security method gets shown).
  */
 @SysUISingleton
-@ExperimentalCoroutinesApi
 public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable {
 
     static final String TAG = "ScrimController";
@@ -258,6 +257,7 @@
     private int mScrimsVisibility;
     private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener;
     private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
+    private final BlurConfig mBlurConfig;
     private Consumer<Integer> mScrimVisibleListener;
     private boolean mBlankScreen;
     private boolean mScreenBlankingCallbackCalled;
@@ -339,9 +339,11 @@
             KeyguardTransitionInteractor keyguardTransitionInteractor,
             KeyguardInteractor keyguardInteractor,
             @Main CoroutineDispatcher mainDispatcher,
-            LargeScreenShadeInterpolator largeScreenShadeInterpolator) {
+            LargeScreenShadeInterpolator largeScreenShadeInterpolator,
+            BlurConfig blurConfig) {
         mScrimStateListener = lightBarController::setScrimState;
         mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
+        mBlurConfig = blurConfig;
         // All scrims default alpha need to match bouncer background alpha to make sure the
         // transitions involving the bouncer are smooth and don't overshoot the bouncer alpha.
         mDefaultScrimAlpha =
@@ -406,7 +408,7 @@
 
         final ScrimState[] states = ScrimState.values();
         for (int i = 0; i < states.length; i++) {
-            states[i].init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager);
+            states[i].init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager, mBlurConfig);
             states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
             states[i].setDefaultScrimAlpha(mDefaultScrimAlpha);
         }
@@ -868,7 +870,7 @@
      * bounds instead.
      */
     public void setClipsQsScrim(boolean clipScrim) {
-        if (Flags.notificationShadeBlur()) {
+        if (Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) {
             // Never clip scrims when blur is enabled, colors of UI elements are supposed to "add"
             // up across the scrims.
             mClipsQsScrim = false;
@@ -1210,6 +1212,12 @@
 
             dispatchBackScrimState(mScrimBehind.getViewAlpha());
         }
+        if (Flags.bouncerUiRevamp()) {
+            // Blur the notification scrim as needed. The blur is needed only when we show the
+            // expanded shade behind the bouncer. Without it, the notification scrim outline is
+            // visible behind the bouncer.
+            mNotificationsScrim.setBlurRadius(mState.getNotifBlurRadius());
+        }
 
         // We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim.
         boolean hideFlagShowWhenLockedActivities =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 8170e6d..5f423cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -24,6 +24,7 @@
 import com.android.app.tracing.coroutines.TrackTracer;
 import com.android.systemui.Flags;
 import com.android.systemui.dock.DockManager;
+import com.android.systemui.keyguard.ui.transitions.BlurConfig;
 import com.android.systemui.res.R;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.shade.ui.ShadeColors;
@@ -149,7 +150,14 @@
         @Override
         public void prepare(ScrimState previousState) {
             if (Flags.bouncerUiRevamp()) {
-                mBehindAlpha = 0f;
+                if (previousState == SHADE_LOCKED) {
+                    mBehindAlpha = previousState.getBehindAlpha();
+                    mNotifAlpha = previousState.getNotifAlpha();
+                    mNotifBlurRadius = mBlurConfig.getMaxBlurRadiusPx();
+                } else {
+                    mNotifAlpha = 0f;
+                    mBehindAlpha = 0f;
+                }
                 mFrontAlpha = TRANSPARENT_BOUNCER_SCRIM_ALPHA;
                 mFrontTint = mSurfaceColor;
                 return;
@@ -395,6 +403,7 @@
     DozeParameters mDozeParameters;
     DockManager mDockManager;
     boolean mDisplayRequiresBlanking;
+    protected BlurConfig mBlurConfig;
     boolean mLaunchingAffordanceWithPreview;
     boolean mOccludeAnimationPlaying;
     boolean mWakeLockScreenSensorActive;
@@ -403,8 +412,12 @@
     boolean mClipQsScrim;
     int mBackgroundColor;
 
+    // This is needed to blur the scrim behind the scrimmed bouncer to avoid showing
+    // the notification section border
+    protected float mNotifBlurRadius = 0.0f;
+
     public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters,
-            DockManager dockManager) {
+            DockManager dockManager, BlurConfig blurConfig) {
         mBackgroundColor = scrimBehind.getContext().getColor(R.color.shade_scrim_background_dark);
         mScrimInFront = scrimInFront;
         mScrimBehind = scrimBehind;
@@ -412,6 +425,7 @@
         mDozeParameters = dozeParameters;
         mDockManager = dockManager;
         mDisplayRequiresBlanking = dozeParameters.getDisplayNeedsBlanking();
+        mBlurConfig = blurConfig;
     }
 
     /** Prepare state for transition. */
@@ -518,4 +532,8 @@
     public void setClipQsScrim(boolean clipsQsScrim) {
         mClipQsScrim = clipsQsScrim;
     }
+
+    public float getNotifBlurRadius() {
+        return mNotifBlurRadius;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 8443edd..b2c4ef9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -111,7 +111,6 @@
 import kotlin.Unit;
 
 import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
 import kotlinx.coroutines.Job;
 
 import java.io.PrintWriter;
@@ -130,7 +129,7 @@
  * which is in turn, reported to this class by the current
  * {@link com.android.keyguard.KeyguardViewController}.
  */
-@ExperimentalCoroutinesApi @SysUISingleton
+@SysUISingleton
 public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
         StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener,
         ShadeExpansionListener, NavigationModeController.ModeChangedListener,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
index 705a11d..e12b21e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.statusbar.phone
 
 import android.view.View
+import com.android.systemui.Flags
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.TransitionAnimator
 import com.android.systemui.animation.TransitionAnimator.Companion.getProgress
@@ -22,7 +23,7 @@
     private val notificationShadeWindowController: NotificationShadeWindowController,
     private val commandQueue: CommandQueue,
     @DisplayId private val displayId: Int,
-    private val isLaunchForActivity: Boolean = true
+    private val isLaunchForActivity: Boolean = true,
 ) : ActivityTransitionAnimator.Controller by delegate {
     private var hideIconsDuringLaunchAnimation: Boolean = true
 
@@ -41,8 +42,16 @@
     }
 
     override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
-        delegate.onTransitionAnimationStart(isExpandingFullyAbove)
-        shadeAnimationInteractor.setIsLaunchingActivity(true)
+        if (Flags.shadeLaunchAccessibility()) {
+            // We set this before calling the delegate to make sure that accessibility is disabled
+            // for the whole duration of the transition, so that we don't have stray TalkBack events
+            // once the animating view becomes invisible.
+            shadeAnimationInteractor.setIsLaunchingActivity(true)
+            delegate.onTransitionAnimationStart(isExpandingFullyAbove)
+        } else {
+            delegate.onTransitionAnimationStart(isExpandingFullyAbove)
+            shadeAnimationInteractor.setIsLaunchingActivity(true)
+        }
         if (!isExpandingFullyAbove) {
             shadeController.collapseWithDuration(
                 ActivityTransitionAnimator.TIMINGS.totalDuration.toInt()
@@ -59,7 +68,7 @@
     override fun onTransitionAnimationProgress(
         state: TransitionAnimator.State,
         progress: Float,
-        linearProgress: Float
+        linearProgress: Float,
     ) {
         delegate.onTransitionAnimationProgress(state, progress, linearProgress)
         val hideIcons =
@@ -67,7 +76,7 @@
                 ActivityTransitionAnimator.TIMINGS,
                 linearProgress,
                 ANIMATION_DELAY_ICON_FADE_IN,
-                100
+                100,
             ) == 0.0f
         if (hideIcons != hideIconsDuringLaunchAnimation) {
             hideIconsDuringLaunchAnimation = hideIcons
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
index 99141f5..ba7628f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -56,7 +55,6 @@
  *   visible
  * * - [OngoingCallModel.InCall] when there is a call notification and the call app is not visible
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class OngoingCallInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
index 9f1395a..f82e681 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
@@ -81,12 +81,7 @@
                 awaitClose { observer.isListening = false }
             }
             .distinctUntilChanged()
-            .logDiffsForTable(
-                logger,
-                columnPrefix = "",
-                columnName = "isAirplaneMode",
-                initialValue = false,
-            )
+            .logDiffsForTable(logger, columnName = "isAirplaneMode", initialValue = false)
             .stateIn(
                 scope,
                 started = SharingStarted.WhileSubscribed(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
index bd18f4b..2cef54f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
@@ -59,7 +59,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 logger,
-                columnPrefix = "",
                 columnName = "isAirplaneModeIconVisible",
                 initialValue = false,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index 252ebe6..66587c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -62,7 +61,6 @@
  * implementation.
  */
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MobileRepositorySwitcher
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
index be3977e..410389a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
@@ -64,9 +64,8 @@
         _carrierId
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_CARRIER_ID,
-                _carrierId.value,
+                initialValue = _carrierId.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _carrierId.value)
 
@@ -75,9 +74,8 @@
         _inflateSignalStrength
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = "inflate",
-                _inflateSignalStrength.value,
+                initialValue = _inflateSignalStrength.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _inflateSignalStrength.value)
 
@@ -89,9 +87,8 @@
         _isEmergencyOnly
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_EMERGENCY,
-                _isEmergencyOnly.value,
+                initialValue = _isEmergencyOnly.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _isEmergencyOnly.value)
 
@@ -100,9 +97,8 @@
         _isRoaming
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_ROAMING,
-                _isRoaming.value,
+                initialValue = _isRoaming.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _isRoaming.value)
 
@@ -111,9 +107,8 @@
         _operatorAlphaShort
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_OPERATOR,
-                _operatorAlphaShort.value,
+                initialValue = _operatorAlphaShort.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _operatorAlphaShort.value)
 
@@ -122,9 +117,8 @@
         _isInService
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_IS_IN_SERVICE,
-                _isInService.value,
+                initialValue = _isInService.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _isInService.value)
 
@@ -133,21 +127,15 @@
         _isNonTerrestrial
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_IS_NTN,
-                _isNonTerrestrial.value,
+                initialValue = _isNonTerrestrial.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _isNonTerrestrial.value)
 
     private val _isGsm = MutableStateFlow(false)
     override val isGsm =
         _isGsm
-            .logDiffsForTable(
-                tableLogBuffer,
-                columnPrefix = "",
-                columnName = COL_IS_GSM,
-                _isGsm.value,
-            )
+            .logDiffsForTable(tableLogBuffer, columnName = COL_IS_GSM, initialValue = _isGsm.value)
             .stateIn(scope, SharingStarted.WhileSubscribed(), _isGsm.value)
 
     private val _cdmaLevel = MutableStateFlow(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
@@ -155,9 +143,8 @@
         _cdmaLevel
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_CDMA_LEVEL,
-                _cdmaLevel.value,
+                initialValue = _cdmaLevel.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _cdmaLevel.value)
 
@@ -166,9 +153,8 @@
         _primaryLevel
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_PRIMARY_LEVEL,
-                _primaryLevel.value,
+                initialValue = _primaryLevel.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _primaryLevel.value)
 
@@ -177,23 +163,22 @@
         _satelliteLevel
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_SATELLITE_LEVEL,
-                _satelliteLevel.value,
+                initialValue = _satelliteLevel.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _satelliteLevel.value)
 
     private val _dataConnectionState = MutableStateFlow(DataConnectionState.Disconnected)
     override val dataConnectionState =
         _dataConnectionState
-            .logDiffsForTable(tableLogBuffer, columnPrefix = "", _dataConnectionState.value)
+            .logDiffsForTable(tableLogBuffer, initialValue = _dataConnectionState.value)
             .stateIn(scope, SharingStarted.WhileSubscribed(), _dataConnectionState.value)
 
     private val _dataActivityDirection =
         MutableStateFlow(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
     override val dataActivityDirection =
         _dataActivityDirection
-            .logDiffsForTable(tableLogBuffer, columnPrefix = "", _dataActivityDirection.value)
+            .logDiffsForTable(tableLogBuffer, initialValue = _dataActivityDirection.value)
             .stateIn(scope, SharingStarted.WhileSubscribed(), _dataActivityDirection.value)
 
     private val _carrierNetworkChangeActive = MutableStateFlow(false)
@@ -201,9 +186,8 @@
         _carrierNetworkChangeActive
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_CARRIER_NETWORK_CHANGE,
-                _carrierNetworkChangeActive.value,
+                initialValue = _carrierNetworkChangeActive.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _carrierNetworkChangeActive.value)
 
@@ -211,7 +195,7 @@
         MutableStateFlow(ResolvedNetworkType.UnknownNetworkType)
     override val resolvedNetworkType =
         _resolvedNetworkType
-            .logDiffsForTable(tableLogBuffer, columnPrefix = "", _resolvedNetworkType.value)
+            .logDiffsForTable(tableLogBuffer, initialValue = _resolvedNetworkType.value)
             .stateIn(scope, SharingStarted.WhileSubscribed(), _resolvedNetworkType.value)
 
     override val numberOfLevels =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index b608e53..987173e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -39,7 +39,6 @@
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -54,7 +53,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** This repository vends out data based on demo mode commands */
-@OptIn(ExperimentalCoroutinesApi::class)
 class DemoMobileConnectionsRepository
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index 98aa7fa..5094bc7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -28,7 +28,6 @@
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -46,7 +45,6 @@
  * carrier merged (see [setIsCarrierMerged]).
  */
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
 class FullMobileConnectionRepository(
     override val subId: Int,
     startingIsCarrierMerged: Boolean,
@@ -75,7 +73,6 @@
         _isCarrierMerged
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = "isCarrierMerged",
                 initialValue = startingIsCarrierMerged,
             )
@@ -130,9 +127,8 @@
             .flatMapLatest { it.isEmergencyOnly }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_EMERGENCY,
-                activeRepo.value.isEmergencyOnly.value,
+                initialValue = activeRepo.value.isEmergencyOnly.value,
             )
             .stateIn(
                 scope,
@@ -145,9 +141,8 @@
             .flatMapLatest { it.isRoaming }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_ROAMING,
-                activeRepo.value.isRoaming.value,
+                initialValue = activeRepo.value.isRoaming.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isRoaming.value)
 
@@ -156,9 +151,8 @@
             .flatMapLatest { it.operatorAlphaShort }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_OPERATOR,
-                activeRepo.value.operatorAlphaShort.value,
+                initialValue = activeRepo.value.operatorAlphaShort.value,
             )
             .stateIn(
                 scope,
@@ -171,9 +165,8 @@
             .flatMapLatest { it.isInService }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_IS_IN_SERVICE,
-                activeRepo.value.isInService.value,
+                initialValue = activeRepo.value.isInService.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isInService.value)
 
@@ -182,9 +175,8 @@
             .flatMapLatest { it.isNonTerrestrial }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_IS_NTN,
-                activeRepo.value.isNonTerrestrial.value,
+                initialValue = activeRepo.value.isNonTerrestrial.value,
             )
             .stateIn(
                 scope,
@@ -197,9 +189,8 @@
             .flatMapLatest { it.isGsm }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_IS_GSM,
-                activeRepo.value.isGsm.value,
+                initialValue = activeRepo.value.isGsm.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isGsm.value)
 
@@ -208,9 +199,8 @@
             .flatMapLatest { it.cdmaLevel }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_CDMA_LEVEL,
-                activeRepo.value.cdmaLevel.value,
+                initialValue = activeRepo.value.cdmaLevel.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.cdmaLevel.value)
 
@@ -219,9 +209,8 @@
             .flatMapLatest { it.primaryLevel }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_PRIMARY_LEVEL,
-                activeRepo.value.primaryLevel.value,
+                initialValue = activeRepo.value.primaryLevel.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.primaryLevel.value)
 
@@ -230,9 +219,8 @@
             .flatMapLatest { it.satelliteLevel }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_SATELLITE_LEVEL,
-                activeRepo.value.satelliteLevel.value,
+                initialValue = activeRepo.value.satelliteLevel.value,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.satelliteLevel.value)
 
@@ -241,8 +229,7 @@
             .flatMapLatest { it.dataConnectionState }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
-                activeRepo.value.dataConnectionState.value,
+                initialValue = activeRepo.value.dataConnectionState.value,
             )
             .stateIn(
                 scope,
@@ -255,8 +242,7 @@
             .flatMapLatest { it.dataActivityDirection }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
-                activeRepo.value.dataActivityDirection.value,
+                initialValue = activeRepo.value.dataActivityDirection.value,
             )
             .stateIn(
                 scope,
@@ -269,9 +255,8 @@
             .flatMapLatest { it.carrierNetworkChangeActive }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = COL_CARRIER_NETWORK_CHANGE,
-                activeRepo.value.carrierNetworkChangeActive.value,
+                initialValue = activeRepo.value.carrierNetworkChangeActive.value,
             )
             .stateIn(
                 scope,
@@ -284,8 +269,7 @@
             .flatMapLatest { it.resolvedNetworkType }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
-                activeRepo.value.resolvedNetworkType.value,
+                initialValue = activeRepo.value.resolvedNetworkType.value,
             )
             .stateIn(
                 scope,
@@ -298,7 +282,6 @@
             .flatMapLatest { it.dataEnabled }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = "dataEnabled",
                 initialValue = activeRepo.value.dataEnabled.value,
             )
@@ -309,7 +292,6 @@
             .flatMapLatest { it.inflateSignalStrength }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = "inflate",
                 initialValue = activeRepo.value.inflateSignalStrength.value,
             )
@@ -324,7 +306,6 @@
             .flatMapLatest { it.allowNetworkSliceIndicator }
             .logDiffsForTable(
                 tableLogBuffer,
-                columnPrefix = "",
                 columnName = "allowSlice",
                 initialValue = activeRepo.value.allowNetworkSliceIndicator.value,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index d69dc1e..b4a45e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -68,7 +68,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.asExecutor
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
@@ -92,7 +91,6 @@
  * connection -- see [CarrierMergedConnectionRepository]).
  */
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
 class MobileConnectionRepositoryImpl(
     override val subId: Int,
     private val context: Context,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index aa6da61..d336903 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -62,7 +62,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.asExecutor
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
@@ -81,7 +80,6 @@
 import kotlinx.coroutines.withContext
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MobileConnectionsRepositoryImpl
 @Inject
@@ -382,7 +380,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 tableLogger,
-                columnPrefix = "",
                 columnName = "defaultConnectionIsValidated",
                 initialValue = false,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 2a9a199..a1f7a81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -135,7 +134,6 @@
 
 /** Interactor for a single mobile connection. This connection _should_ have one subscription ID */
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
 class MobileIconInteractorImpl(
     @Background scope: CoroutineScope,
     defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
@@ -240,7 +238,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 tableLogBuffer = tableLogBuffer,
-                columnPrefix = "",
                 initialValue = DefaultIcon(defaultMobileIconGroup.value),
             )
             .stateIn(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index be56461..ac3728d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -39,7 +39,6 @@
 import java.lang.ref.WeakReference
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -128,7 +127,6 @@
 }
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MobileIconsInteractorImpl
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 0bd3426..171e4f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -74,7 +73,6 @@
  * model gets the exact same information, as well as allows us to log that unified state only once
  * per icon.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 class MobileIconViewModel(
     override val subscriptionId: Int,
     iconInteractor: MobileIconInteractor,
@@ -164,7 +162,6 @@
 
 /** Terrestrial (cellular) icon. */
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
 private class CellularIconViewModel(
     override val subscriptionId: Int,
     iconInteractor: MobileIconInteractor,
@@ -194,7 +191,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 iconInteractor.tableLogBuffer,
-                columnPrefix = "",
                 columnName = "visible",
                 initialValue = false,
             )
@@ -252,7 +248,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 iconInteractor.tableLogBuffer,
-                columnPrefix = "",
                 columnName = "showNetworkTypeIcon",
                 initialValue = false,
             )
@@ -296,7 +291,6 @@
         iconInteractor.isRoaming
             .logDiffsForTable(
                 iconInteractor.tableLogBuffer,
-                columnPrefix = "",
                 columnName = "roaming",
                 initialValue = false,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 67fdb3a..22feb7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -33,7 +33,6 @@
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.flow.SharingStarted
@@ -49,7 +48,6 @@
  * of [MobileIconViewModel]s which describe the individual icons and can be bound to
  * [ModernStatusBarMobileView].
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MobileIconsViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
index 70abb02..de2a645 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -51,7 +50,6 @@
  * DemoRepository
  * ```
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class DeviceBasedSatelliteRepositorySwitcher
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index 3a6716a..982f6ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -54,7 +54,6 @@
 import kotlin.coroutines.resume
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.asExecutor
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.delay
@@ -100,7 +99,6 @@
      */
     data object NotSupported : SatelliteSupport
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     companion object {
         /**
          * Convenience function to switch to the supported flow. [retrySignal] is a flow that emits
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index 86f9c94..569e02c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
@@ -64,12 +63,7 @@
                 flowOf(false)
             }
             .distinctUntilChanged()
-            .logDiffsForTable(
-                tableLog,
-                columnPrefix = "",
-                columnName = COL_ALLOWED,
-                initialValue = false,
-            )
+            .logDiffsForTable(tableLog, columnName = COL_ALLOWED, initialValue = false)
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     /** See [SatelliteConnectionState] for relevant states */
@@ -81,11 +75,7 @@
                 flowOf(SatelliteConnectionState.Off)
             }
             .distinctUntilChanged()
-            .logDiffsForTable(
-                tableLog,
-                columnPrefix = "",
-                initialValue = SatelliteConnectionState.Off,
-            )
+            .logDiffsForTable(tableLog, initialValue = SatelliteConnectionState.Off)
             .stateIn(scope, SharingStarted.WhileSubscribed(), SatelliteConnectionState.Off)
 
     /** 0-4 description of the connection strength */
@@ -96,7 +86,7 @@
                 flowOf(0)
             }
             .distinctUntilChanged()
-            .logDiffsForTable(tableLog, columnPrefix = "", columnName = COL_LEVEL, initialValue = 0)
+            .logDiffsForTable(tableLog, columnName = COL_LEVEL, initialValue = 0)
             .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
 
     val isSatelliteProvisioned = repo.isSatelliteProvisioned
@@ -120,12 +110,7 @@
                 isOosAndNotEmergencyAndNotSatellite.all { it }
             }
             .distinctUntilChanged()
-            .logDiffsForTable(
-                tableLog,
-                columnPrefix = "",
-                columnName = COL_ALL_OOS,
-                initialValue = true,
-            )
+            .logDiffsForTable(tableLog, columnName = COL_ALL_OOS, initialValue = true)
 
     /** When all connections are considered OOS, satellite connectivity is potentially valid */
     val areAllConnectionsOutOfService =
@@ -153,12 +138,7 @@
                 flowOf(false)
             }
             .distinctUntilChanged()
-            .logDiffsForTable(
-                tableLog,
-                columnPrefix = "",
-                columnName = COL_FULL_OOS,
-                initialValue = true,
-            )
+            .logDiffsForTable(tableLog, columnName = COL_FULL_OOS, initialValue = true)
             .stateIn(scope, SharingStarted.WhileSubscribed(), true)
 
     /** True if any known mobile network is currently using a non terrestrial network */
@@ -190,7 +170,6 @@
  * [defaultValue] allows for a default value to be used if there are no leaf nodes after applying
  * [selector]. E.g., if there are no mobile connections, assume that there is no service.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 private inline fun <R, reified S, T> Flow<List<R>>.aggregateOver(
     crossinline selector: (R) -> Flow<S>,
     defaultValue: T,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index ea915ef..270a28e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -34,7 +34,6 @@
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -62,7 +61,6 @@
     val carrierText: StateFlow<String?>
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class DeviceBasedSatelliteViewModelImpl
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/HomeStatusBarInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/HomeStatusBarInteractor.kt
index b94ef03..54916b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/HomeStatusBarInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/HomeStatusBarInteractor.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.CarrierConfigInteractor
 import com.android.systemui.statusbar.pipeline.shared.domain.model.StatusBarDisableFlagsVisibilityModel
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
@@ -33,7 +32,6 @@
  * Interactor for the home screen status bar (aka
  * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment]).
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class HomeStatusBarInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index 7e76d77..bd69060 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
@@ -115,6 +116,17 @@
                     }
                 }
 
+                if (com.android.media.projection.flags.Flags.showStopDialogPostCallEnd()) {
+                    launch {
+                        viewModel.mediaProjectionStopDialogDueToCallEndedState.collect { stopDialog
+                            ->
+                            if (stopDialog is MediaProjectionStopDialogModel.Shown) {
+                                stopDialog.createAndShowDialog()
+                            }
+                        }
+                    }
+                }
+
                 if (!StatusBarNotifChips.isEnabled && !StatusBarChipsModernization.isEnabled) {
                     val primaryChipViewBinding =
                         OngoingActivityChipBinder.createBinding(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index 5acedf1..b116b47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -36,7 +36,9 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
@@ -96,6 +98,12 @@
     val transitionFromLockscreenToDreamStartedEvent: Flow<Unit>
 
     /**
+     * The current media projection stop dialog to be shown, or
+     * `MediaProjectionStopDialogModel.Hidden` if no dialog is visible.
+     */
+    val mediaProjectionStopDialogDueToCallEndedState: StateFlow<MediaProjectionStopDialogModel>
+
+    /**
      * The ongoing activity chip that should be primarily shown on the left-hand side of the status
      * bar. If there are multiple ongoing activity chips, this one should take priority.
      */
@@ -180,6 +188,7 @@
     sceneInteractor: SceneInteractor,
     sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
     shadeInteractor: ShadeInteractor,
+    shareToAppChipViewModel: ShareToAppChipViewModel,
     ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
     statusBarPopupChipsViewModel: StatusBarPopupChipsViewModel,
     animations: SystemStatusEventAnimationInteractor,
@@ -195,7 +204,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 tableLogBuffer = tableLogger,
-                columnPrefix = "",
                 columnName = COL_LOCK_TO_OCCLUDED,
                 initialValue = false,
             )
@@ -207,6 +215,9 @@
             .filter { it.transitionState == TransitionState.STARTED }
             .map {}
 
+    override val mediaProjectionStopDialogDueToCallEndedState =
+        shareToAppChipViewModel.stopDialogToShow
+
     override val primaryOngoingActivityChip = ongoingActivityChipsViewModel.primaryChip
 
     override val ongoingActivityChips = ongoingActivityChipsViewModel.chips
@@ -228,7 +239,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 tableLogBuffer = tableLogger,
-                columnPrefix = "",
                 columnName = COL_ALLOWED_BY_SCENE,
                 initialValue = false,
             )
@@ -248,7 +258,6 @@
             }
             .logDiffsForTable(
                 tableLogBuffer = tableLogger,
-                columnPrefix = "",
                 columnName = COL_NOTIF_LIGHTS_OUT,
                 initialValue = false,
             )
@@ -306,7 +315,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 tableLogBuffer = tableLogger,
-                columnPrefix = "",
                 columnName = COL_VISIBLE,
                 initialValue = false,
             )
@@ -350,7 +358,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 tableLogBuffer = tableLogger,
-                columnPrefix = "",
                 columnName = COL_SHOW_OPERATOR_NAME,
                 initialValue = false,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
index 352413e..6258a55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
@@ -38,7 +38,6 @@
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -52,7 +51,6 @@
  * InternetTileModel objects, so that updating the tile is as simple as collecting on this state
  * flow and then calling [QSTileImpl.refreshState]
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class InternetTileViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt
index 0b83c4e..6ea5fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt
@@ -19,7 +19,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
@@ -28,7 +27,6 @@
  * View model for the operator name (aka carrier name) of the carrier for the default data
  * subscription.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class StatusBarOperatorNameViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
index b6e01e8..8d1edd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -54,7 +53,6 @@
  * that flow.
  */
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class WifiRepositorySwitcher
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index 6ed2ce8..f9bba9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -109,7 +109,6 @@
             secondaryNetworks = emptyList(),
         )
 
-    @kotlinx.coroutines.ExperimentalCoroutinesApi
     private val wifiPickerTrackerInfo: StateFlow<WifiPickerTrackerInfo> =
         if (multiuserWifiPickerTrackerSupport()) {
             selectedUserContext
@@ -301,19 +300,14 @@
         wifiPickerTrackerInfo
             .map { it.state == WifiManager.WIFI_STATE_ENABLED }
             .distinctUntilChanged()
-            .logDiffsForTable(
-                tableLogger,
-                columnPrefix = "",
-                columnName = COL_NAME_IS_ENABLED,
-                initialValue = false,
-            )
+            .logDiffsForTable(tableLogger, columnName = COL_NAME_IS_ENABLED, initialValue = false)
             .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val wifiNetwork: StateFlow<WifiNetworkModel> =
         wifiPickerTrackerInfo
             .map { it.primaryNetwork }
             .distinctUntilChanged()
-            .logDiffsForTable(tableLogger, columnPrefix = "", initialValue = WIFI_NETWORK_DEFAULT)
+            .logDiffsForTable(tableLogger, initialValue = WIFI_NETWORK_DEFAULT)
             .stateIn(scope, SharingStarted.Eagerly, WIFI_NETWORK_DEFAULT)
 
     override val secondaryNetworks: StateFlow<List<WifiNetworkModel>> =
@@ -322,7 +316,6 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 tableLogger,
-                columnPrefix = "",
                 columnName = "secondaryNetworks",
                 initialValue = emptyList(),
             )
@@ -401,12 +394,7 @@
         wifiPickerTrackerInfo
             .map { it.isDefault }
             .distinctUntilChanged()
-            .logDiffsForTable(
-                tableLogger,
-                columnPrefix = "",
-                columnName = COL_NAME_IS_DEFAULT,
-                initialValue = false,
-            )
+            .logDiffsForTable(tableLogger, columnName = COL_NAME_IS_DEFAULT, initialValue = false)
             .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val wifiActivity: StateFlow<DataActivityModel> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index f9556d2..986068b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -89,7 +89,7 @@
                     else -> WifiIcon.Hidden
                 }
             }
-            .logDiffsForTable(wifiTableLogBuffer, columnPrefix = "", initialValue = WifiIcon.Hidden)
+            .logDiffsForTable(wifiTableLogBuffer, initialValue = WifiIcon.Hidden)
             .stateIn(
                 scope,
                 started = SharingStarted.WhileSubscribed(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 23e40b2..90d21b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -17,11 +17,8 @@
 package com.android.systemui.statusbar.policy;
 
 import android.app.NotificationManager;
-import android.content.ComponentName;
 import android.net.Uri;
-import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
 
 import com.android.systemui.statusbar.policy.ZenModeController.Callback;
 import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor;
@@ -35,25 +32,17 @@
 public interface ZenModeController extends CallbackController<Callback> {
     void setZen(int zen, Uri conditionId, String reason);
     int getZen();
-    ZenRule getManualRule();
     ZenModeConfig getConfig();
     /** Gets consolidated zen policy that will apply when DND is on in priority only mode */
     NotificationManager.Policy getConsolidatedPolicy();
     long getNextAlarm();
     boolean isZenAvailable();
-    ComponentName getEffectsSuppressor();
-    boolean isCountdownConditionSupported();
     int getCurrentUser();
-    boolean isVolumeRestricted();
-    boolean areNotificationsHiddenInShade();
 
     public static interface Callback {
         default void onZenChanged(int zen) {}
-        default void onConditionsChanged(Condition[] conditions) {}
         default void onNextAlarmChanged() {}
         default void onZenAvailableChanged(boolean available) {}
-        default void onEffectsSupressorChanged() {}
-        default void onManualRuleChanged(ZenRule rule) {}
         default void onConfigChanged(ZenModeConfig config) {}
         /** Called when the consolidated zen policy changes */
         default void onConsolidatedPolicyChanged(NotificationManager.Policy policy) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 0cba940..9ad8619 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -20,7 +20,6 @@
 import android.app.Flags;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -35,7 +34,6 @@
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
 import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
 import android.text.format.DateFormat;
 import android.util.Log;
 
@@ -92,7 +90,6 @@
                     }
                     final IntentFilter filter = new IntentFilter(
                             AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
-                    filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
                     mBroadcastDispatcher.registerReceiver(mReceiver, filter, null,
                             UserHandle.of(mUserId));
                     mRegistered = true;
@@ -156,21 +153,6 @@
     }
 
     @Override
-    public boolean isVolumeRestricted() {
-        return mUserManager.hasUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME,
-                UserHandle.of(mUserId));
-    }
-
-    @Override
-    public boolean areNotificationsHiddenInShade() {
-        if (mZenMode != Global.ZEN_MODE_OFF) {
-            return (mConsolidatedNotificationPolicy.suppressedVisualEffects
-                    & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
-        }
-        return false;
-    }
-
-    @Override
     public void addCallback(@NonNull Callback callback) {
         synchronized (mCallbacksLock) {
             Log.d(TAG, "Added callback " + callback.getClass());
@@ -206,11 +188,6 @@
     }
 
     @Override
-    public ZenRule getManualRule() {
-        return mConfig == null ? null : mConfig.manualRule;
-    }
-
-    @Override
     public ZenModeConfig getConfig() {
         return mConfig;
     }
@@ -228,17 +205,6 @@
     }
 
     @Override
-    public ComponentName getEffectsSuppressor() {
-        return NotificationManager.from(mContext).getEffectsSuppressor();
-    }
-
-    @Override
-    public boolean isCountdownConditionSupported() {
-        return NotificationManager.from(mContext)
-                .isSystemConditionProviderEnabled(ZenModeConfig.COUNTDOWN_PATH);
-    }
-
-    @Override
     public int getCurrentUser() {
         return mUserTracker.getUserId();
     }
@@ -247,10 +213,6 @@
         fireSafeChange(Callback::onNextAlarmChanged);
     }
 
-    private void fireEffectsSuppressorChanged() {
-        fireSafeChange(Callback::onEffectsSupressorChanged);
-    }
-
     private void fireZenChanged(int zen) {
         fireSafeChange(c -> c.onZenChanged(zen));
     }
@@ -259,10 +221,6 @@
         fireSafeChange(c -> c.onZenAvailableChanged(available));
     }
 
-    private void fireManualRuleChanged(ZenRule rule) {
-        fireSafeChange(c -> c.onManualRuleChanged(rule));
-    }
-
     private void fireConsolidatedPolicyChanged(NotificationManager.Policy policy) {
         fireSafeChange(c -> c.onConsolidatedPolicyChanged(policy));
     }
@@ -302,16 +260,10 @@
     protected void updateZenModeConfig() {
         final ZenModeConfig config = mNoMan.getZenModeConfig();
         if (Objects.equals(config, mConfig)) return;
-        final ZenRule oldRule = mConfig != null ? mConfig.manualRule : null;
         mConfig = config;
         mZenUpdateTime = System.currentTimeMillis();
         fireConfigChanged(config);
 
-        final ZenRule newRule = config != null ? config.manualRule : null;
-        if (!Objects.equals(oldRule, newRule)) {
-            fireManualRuleChanged(newRule);
-        }
-
         final NotificationManager.Policy consolidatedPolicy =
                 mNoMan.getConsolidatedNotificationPolicy();
         if (!Objects.equals(consolidatedPolicy, mConsolidatedNotificationPolicy)) {
@@ -327,9 +279,6 @@
             if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(intent.getAction())) {
                 fireNextAlarmChanged();
             }
-            if (NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED.equals(intent.getAction())) {
-                fireEffectsSuppressorChanged();
-            }
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepository.kt
index 73cdf48..82d57f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepository.kt
@@ -23,7 +23,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -41,7 +40,6 @@
     val isUserSetUp: StateFlow<Boolean>
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class UserSetupRepositoryImpl
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 69b7e89..eecea92 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -27,6 +27,7 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.safeDrawingPadding
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
@@ -49,8 +50,10 @@
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import com.android.systemui.inputdevice.tutorial.ui.composable.DoneButton
+import com.android.systemui.keyboard.shortcut.ui.composable.hasCompactWindowSize
 import com.android.systemui.res.R
 import com.android.systemui.touchpad.tutorial.ui.gesture.isFourFingerTouchpadSwipe
 import com.android.systemui.touchpad.tutorial.ui.gesture.isThreeFingerTouchpadSwipe
@@ -80,17 +83,29 @@
                     }
                 ),
     ) {
+        val isCompactWindow = hasCompactWindowSize()
+        val padding = if (isCompactWindow) 24.dp else 60.dp
         val configuration = LocalConfiguration.current
         when (configuration.orientation) {
             Configuration.ORIENTATION_LANDSCAPE -> {
-                HorizontalSelectionButtons(
-                    onBackTutorialClicked = onBackTutorialClicked,
-                    onHomeTutorialClicked = onHomeTutorialClicked,
-                    onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
-                    onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
-                    modifier = Modifier.weight(1f).padding(60.dp),
-                    lastSelectedScreen,
-                )
+                if (isCompactWindow)
+                    HorizontalCompactSelectionButtons(
+                        onBackTutorialClicked = onBackTutorialClicked,
+                        onHomeTutorialClicked = onHomeTutorialClicked,
+                        onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
+                        onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
+                        lastSelectedScreen,
+                        modifier = Modifier.weight(1f).padding(padding),
+                    )
+                else
+                    HorizontalSelectionButtons(
+                        onBackTutorialClicked = onBackTutorialClicked,
+                        onHomeTutorialClicked = onHomeTutorialClicked,
+                        onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
+                        onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
+                        lastSelectedScreen,
+                        modifier = Modifier.weight(1f).padding(padding),
+                    )
             }
             else -> {
                 VerticalSelectionButtons(
@@ -98,15 +113,15 @@
                     onHomeTutorialClicked = onHomeTutorialClicked,
                     onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
                     onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
-                    modifier = Modifier.weight(1f).padding(60.dp),
                     lastSelectedScreen,
+                    modifier = Modifier.weight(1f).padding(padding),
                 )
             }
         }
         // because other composables have weight 1, Done button will be positioned first
         DoneButton(
             onDoneButtonClicked = onDoneButtonClicked,
-            modifier = Modifier.padding(horizontal = 60.dp),
+            modifier = Modifier.padding(horizontal = padding),
         )
     }
 }
@@ -117,11 +132,99 @@
     onHomeTutorialClicked: () -> Unit,
     onRecentAppsTutorialClicked: () -> Unit,
     onSwitchAppsTutorialClicked: () -> Unit,
-    modifier: Modifier = Modifier,
     lastSelectedScreen: Screen,
+    modifier: Modifier = Modifier,
+) {
+    Column(modifier = modifier) {
+        TwoByTwoTutorialButtons(
+            onBackTutorialClicked,
+            onHomeTutorialClicked,
+            onRecentAppsTutorialClicked,
+            onSwitchAppsTutorialClicked,
+            lastSelectedScreen,
+            modifier = Modifier.weight(1f).fillMaxSize(),
+        )
+    }
+}
+
+@Composable
+private fun TwoByTwoTutorialButtons(
+    onBackTutorialClicked: () -> Unit,
+    onHomeTutorialClicked: () -> Unit,
+    onRecentAppsTutorialClicked: () -> Unit,
+    onSwitchAppsTutorialClicked: () -> Unit,
+    lastSelectedScreen: Screen,
+    modifier: Modifier = Modifier,
+) {
+    val homeFocusRequester = remember { FocusRequester() }
+    val backFocusRequester = remember { FocusRequester() }
+    val recentAppsFocusRequester = remember { FocusRequester() }
+    val switchAppsFocusRequester = remember { FocusRequester() }
+    LaunchedEffect(Unit) {
+        when (lastSelectedScreen) {
+            Screen.HOME_GESTURE -> homeFocusRequester.requestFocus()
+            Screen.BACK_GESTURE -> backFocusRequester.requestFocus()
+            Screen.RECENT_APPS_GESTURE -> recentAppsFocusRequester.requestFocus()
+            Screen.SWITCH_APPS_GESTURE -> switchAppsFocusRequester.requestFocus()
+            else -> {} // No-Op.
+        }
+    }
+    Column {
+        Row(Modifier.weight(1f)) {
+            TutorialButton(
+                text = stringResource(R.string.touchpad_tutorial_home_gesture_button),
+                icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_home_icon),
+                iconColor = MaterialTheme.colorScheme.onPrimary,
+                onClick = onHomeTutorialClicked,
+                backgroundColor = MaterialTheme.colorScheme.primary,
+                modifier = modifier.focusRequester(homeFocusRequester).focusable().fillMaxSize(),
+            )
+            Spacer(modifier = Modifier.size(16.dp))
+            TutorialButton(
+                text = stringResource(R.string.touchpad_tutorial_back_gesture_button),
+                icon = Icons.AutoMirrored.Outlined.ArrowBack,
+                iconColor = MaterialTheme.colorScheme.onTertiary,
+                onClick = onBackTutorialClicked,
+                backgroundColor = MaterialTheme.colorScheme.tertiary,
+                modifier = modifier.focusRequester(backFocusRequester).focusable().fillMaxSize(),
+            )
+        }
+        Spacer(modifier = Modifier.size(16.dp))
+        Row(Modifier.weight(1f)) {
+            TutorialButton(
+                text = stringResource(R.string.touchpad_tutorial_recent_apps_gesture_button),
+                icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_recents_icon),
+                iconColor = MaterialTheme.colorScheme.onSecondary,
+                onClick = onRecentAppsTutorialClicked,
+                backgroundColor = MaterialTheme.colorScheme.secondary,
+                modifier =
+                    modifier.focusRequester(recentAppsFocusRequester).focusable().fillMaxSize(),
+            )
+            Spacer(modifier = Modifier.size(16.dp))
+            TutorialButton(
+                text = stringResource(R.string.touchpad_tutorial_switch_apps_gesture_button),
+                icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_apps_icon),
+                iconColor = MaterialTheme.colorScheme.primary,
+                onClick = onSwitchAppsTutorialClicked,
+                backgroundColor = MaterialTheme.colorScheme.onPrimary,
+                modifier =
+                    modifier.focusRequester(switchAppsFocusRequester).focusable().fillMaxSize(),
+            )
+        }
+    }
+}
+
+@Composable
+private fun HorizontalCompactSelectionButtons(
+    onBackTutorialClicked: () -> Unit,
+    onHomeTutorialClicked: () -> Unit,
+    onRecentAppsTutorialClicked: () -> Unit,
+    onSwitchAppsTutorialClicked: () -> Unit,
+    lastSelectedScreen: Screen,
+    modifier: Modifier = Modifier,
 ) {
     Row(
-        horizontalArrangement = Arrangement.spacedBy(20.dp),
+        horizontalArrangement = Arrangement.spacedBy(16.dp),
         verticalAlignment = Alignment.CenterVertically,
         modifier = modifier,
     ) {
@@ -130,8 +233,8 @@
             onHomeTutorialClicked,
             onRecentAppsTutorialClicked,
             onSwitchAppsTutorialClicked,
-            modifier = Modifier.weight(1f).fillMaxSize(),
             lastSelectedScreen,
+            modifier = Modifier.weight(1f).fillMaxSize(),
         )
     }
 }
@@ -142,11 +245,11 @@
     onHomeTutorialClicked: () -> Unit,
     onRecentAppsTutorialClicked: () -> Unit,
     onSwitchAppsTutorialClicked: () -> Unit,
-    modifier: Modifier = Modifier,
     lastSelectedScreen: Screen,
+    modifier: Modifier = Modifier,
 ) {
     Column(
-        verticalArrangement = Arrangement.spacedBy(20.dp),
+        verticalArrangement = Arrangement.spacedBy(16.dp),
         horizontalAlignment = Alignment.CenterHorizontally,
         modifier = modifier,
     ) {
@@ -155,8 +258,8 @@
             onHomeTutorialClicked,
             onRecentAppsTutorialClicked,
             onSwitchAppsTutorialClicked,
-            modifier = Modifier.weight(1f).fillMaxSize(),
             lastSelectedScreen,
+            modifier = Modifier.weight(1f).fillMaxSize(),
         )
     }
 }
@@ -167,8 +270,8 @@
     onHomeTutorialClicked: () -> Unit,
     onRecentAppsTutorialClicked: () -> Unit,
     onSwitchAppsTutorialClicked: () -> Unit,
-    modifier: Modifier = Modifier,
     lastSelectedScreen: Screen,
+    modifier: Modifier = Modifier,
 ) {
     val homeFocusRequester = remember { FocusRequester() }
     val backFocusRequester = remember { FocusRequester() }
@@ -244,8 +347,13 @@
                 modifier = Modifier.width(30.dp).height(30.dp),
                 tint = iconColor,
             )
-            Spacer(modifier = Modifier.height(16.dp))
-            Text(text = text, style = MaterialTheme.typography.headlineLarge, color = iconColor)
+            if (!hasCompactWindowSize()) Spacer(modifier = Modifier.height(16.dp))
+            Text(
+                text = text,
+                textAlign = TextAlign.Center,
+                style = MaterialTheme.typography.headlineLarge,
+                color = iconColor,
+            )
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.kt
index 8e7375f..a9dcdff 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.kt
@@ -25,7 +25,6 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import java.util.function.Consumer
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -43,7 +42,6 @@
 
     private var gestureRecognizer: GestureRecognizer? = null
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     val gestureState: Flow<GestureState> =
         provider.recognizer.flatMapLatest {
             gestureRecognizer = it
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index dccd590..f5aac72 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -46,7 +46,6 @@
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.asCoroutineDispatcher
 import kotlinx.coroutines.flow.filter
@@ -81,7 +80,6 @@
     private val isAodEnabled: Boolean
         get() = keyguardInteractor.isAodAvailable.value
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     override fun start() {
         if (!isDeviceFoldable(context.resources, deviceStateManager)) {
             return
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
index ecc2f18..f990947 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
@@ -58,7 +58,6 @@
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlinx.coroutines.withTimeout
 
-@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
 class FoldLightRevealOverlayAnimation
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
index 53c2d88..af65283 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
@@ -22,12 +22,10 @@
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.mapLatest
 
-@OptIn(ExperimentalCoroutinesApi::class)
 class StatusBarUserChipViewModel
 @Inject
 constructor(private val interactor: UserSwitcherInteractor) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepository.kt
index af03c52..73329b4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepository.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.util.settings.UserSettingsProxy
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
@@ -37,7 +36,6 @@
  * the new value.
  */
 // TODO: b/377244768 - Make internal when UserAwareSecureSettingsRepository can be made internal.
-@OptIn(ExperimentalCoroutinesApi::class)
 abstract class UserAwareSettingsRepository(
     private val userSettings: UserSettingsProxy,
     private val userRepository: UserRepository,
diff --git a/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt
index a5c8af5..8079f1ee2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt
@@ -13,14 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.util.ui
 
 import com.android.systemui.util.ui.AnimatedValue.Animating
 import com.android.systemui.util.ui.AnimatedValue.NotAnimating
 import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.transformLatest
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
index 39b434ad..83b7c18 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.volume.dialog
 
-import android.app.Dialog
 import android.content.Context
 import android.graphics.PixelFormat
 import android.os.Bundle
@@ -24,6 +23,7 @@
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
+import androidx.activity.ComponentDialog
 import com.android.app.tracing.coroutines.coroutineScopeTraced
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -40,7 +40,7 @@
     @Application context: Context,
     private val componentFactory: VolumeDialogComponent.Factory,
     private val visibilityInteractor: VolumeDialogVisibilityInteractor,
-) : Dialog(context, R.style.Theme_SystemUI_Dialog_Volume) {
+) : ComponentDialog(context, R.style.Theme_SystemUI_Dialog_Volume) {
 
     init {
         with(window!!) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
index 203a157..5a69be5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
@@ -28,13 +28,11 @@
 import javax.inject.Inject
 import kotlin.coroutines.resume
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.suspendCancellableCoroutine
 
-@OptIn(ExperimentalCoroutinesApi::class)
 class VolumeDialogPlugin
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index 8c01860..e261cee 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.volume.dialog.domain.interactor
 
 import android.annotation.SuppressLint
+import android.media.AudioManager.RINGER_MODE_NORMAL
+import android.media.AudioManager.RINGER_MODE_SILENT
 import android.os.Handler
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.plugins.VolumeDialogController
@@ -60,10 +62,10 @@
                 awaitClose { volumeDialogController.removeCallback(producer) }
             }
             .buffer(capacity = BUFFER_CAPACITY, onBufferOverflow = BufferOverflow.DROP_OLDEST)
-            .shareIn(replay = 0, scope = coroutineScope, started = SharingStarted.WhileSubscribed())
+            .shareIn(replay = 0, scope = coroutineScope, started = SharingStarted.Eagerly)
             .onStart { emit(VolumeDialogEventModel.SubscribedToEvents) }
 
-    private class VolumeDialogEventModelProducer(
+    private inner class VolumeDialogEventModelProducer(
         private val scope: ProducerScope<VolumeDialogEventModel>
     ) : VolumeDialogController.Callbacks {
         override fun onShowRequested(reason: Int, keyguardLocked: Boolean, lockTaskModeState: Int) {
@@ -93,14 +95,6 @@
         // Configuration change is never emitted by the VolumeDialogControllerImpl now.
         override fun onConfigurationChanged() = Unit
 
-        override fun onShowVibrateHint() {
-            scope.trySend(VolumeDialogEventModel.ShowVibrateHint)
-        }
-
-        override fun onShowSilentHint() {
-            scope.trySend(VolumeDialogEventModel.ShowSilentHint)
-        }
-
         override fun onScreenOff() {
             scope.trySend(VolumeDialogEventModel.ScreenOff)
         }
@@ -113,16 +107,6 @@
             scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream == true))
         }
 
-        // Captions button is remove from the Volume Dialog
-        override fun onCaptionComponentStateChanged(
-            isComponentEnabled: Boolean,
-            fromTooltip: Boolean,
-        ) = Unit
-
-        // Captions button is remove from the Volume Dialog
-        override fun onCaptionEnabledStateChanged(isEnabled: Boolean, checkBeforeSwitch: Boolean) =
-            Unit
-
         override fun onShowCsdWarning(csdWarning: Int, durationMs: Int) {
             scope.trySend(
                 VolumeDialogEventModel.ShowCsdWarning(
@@ -135,5 +119,25 @@
         override fun onVolumeChangedFromKey() {
             scope.trySend(VolumeDialogEventModel.VolumeChangedFromKey)
         }
+
+        // This should've been handled in side the controller itself.
+        override fun onShowVibrateHint() {
+            volumeDialogController.setRingerMode(RINGER_MODE_SILENT, false)
+        }
+
+        // This should've been handled in side the controller itself.
+        override fun onShowSilentHint() {
+            volumeDialogController.setRingerMode(RINGER_MODE_NORMAL, false)
+        }
+
+        // Captions button is remove from the Volume Dialog
+        override fun onCaptionComponentStateChanged(
+            isComponentEnabled: Boolean,
+            fromTooltip: Boolean,
+        ) = Unit
+
+        // Captions button is remove from the Volume Dialog
+        override fun onCaptionEnabledStateChanged(isEnabled: Boolean, checkBeforeSwitch: Boolean) =
+            Unit
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index 7307807..20a74b0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
@@ -31,7 +31,6 @@
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -52,7 +51,6 @@
  * - it might be dismissed by the inactivity timeout;
  * - it can be dismissed by the user;
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @VolumeDialogPluginScope
 class VolumeDialogVisibilityInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
index 9793d2b..a0214dc 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
@@ -38,10 +38,6 @@
 
     data class LayoutDirectionChanged(val layoutDirection: Int) : VolumeDialogEventModel
 
-    data object ShowVibrateHint : VolumeDialogEventModel
-
-    data object ShowSilentHint : VolumeDialogEventModel
-
     data object ScreenOff : VolumeDialogEventModel
 
     data class ShowSafetyWarning(val flags: Int) : VolumeDialogEventModel
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
index 4071918..73f6236 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
@@ -47,7 +47,6 @@
     private val audioSystemRepository: AudioSystemRepository,
     private val ringerFeedbackRepository: VolumeDialogRingerFeedbackRepository,
 ) {
-
     val ringerModel: Flow<VolumeDialogRingerModel> =
         volumeDialogStateInteractor.volumeDialogState
             .mapNotNull { toRingerModel(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index 908249d..2af4b61 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -43,7 +43,6 @@
 import javax.inject.Inject
 import kotlin.properties.Delegates
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.launchIn
@@ -54,7 +53,6 @@
 // Ensure roundness and color of button is updated when progress is changed by a minimum fraction.
 private const val BUTTON_MIN_VISIBLE_CHANGE = 0.05F
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @VolumeDialogScope
 class VolumeDialogRingerViewBinder
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
index 03442db..0e4e707 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.volume.dialog.settings.ui.viewmodel
 
 import android.animation.Animator
@@ -47,7 +45,6 @@
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.resume
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.FlowCollector
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
index 940c79c..577e47b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
 import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogOverscrollViewBinder
-import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderHapticsViewBinder
 import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
 import dagger.BindsInstance
 import dagger.Subcomponent
@@ -33,8 +32,6 @@
 
     fun sliderViewBinder(): VolumeDialogSliderViewBinder
 
-    fun sliderHapticsViewBinder(): VolumeDialogSliderHapticsViewBinder
-
     fun overscrollViewBinder(): VolumeDialogOverscrollViewBinder
 
     @Subcomponent.Factory
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt
index 82885d6..07954f8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.volume.dialog.sliders.data.repository
 
-import android.view.MotionEvent
 import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
+import com.android.systemui.volume.dialog.sliders.shared.model.SliderInputEvent
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -26,10 +26,11 @@
 @VolumeDialogSliderScope
 class VolumeDialogSliderTouchEventsRepository @Inject constructor() {
 
-    private val mutableSliderTouchEvents: MutableStateFlow<MotionEvent?> = MutableStateFlow(null)
-    val sliderTouchEvent: Flow<MotionEvent> = mutableSliderTouchEvents.filterNotNull()
+    private val mutableSliderTouchEvents: MutableStateFlow<SliderInputEvent.Touch?> =
+        MutableStateFlow(null)
+    val sliderTouchEvent: Flow<SliderInputEvent.Touch> = mutableSliderTouchEvents.filterNotNull()
 
-    fun update(event: MotionEvent) {
-        mutableSliderTouchEvents.tryEmit(MotionEvent.obtain(event))
+    fun update(touch: SliderInputEvent.Touch) {
+        mutableSliderTouchEvents.value = touch
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt
index c7b4184..351832bd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.volume.dialog.sliders.domain.interactor
 
-import android.view.MotionEvent
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogCallbacksInteractor
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
@@ -45,7 +44,7 @@
 
     val event: Flow<SliderInputEvent> =
         merge(
-            repository.sliderTouchEvent.map { SliderInputEvent.Touch(it) },
+            repository.sliderTouchEvent,
             volumeDialogCallbacksInteractor.event
                 .filterIsInstance(VolumeDialogEventModel.VolumeChangedFromKey::class)
                 .map { SliderInputEvent.Button },
@@ -55,7 +54,7 @@
         event.onEach { visibilityInteractor.resetDismissTimeout() }.launchIn(coroutineScope)
     }
 
-    fun onTouchEvent(newEvent: MotionEvent) {
-        repository.update(newEvent)
+    fun onTouchEvent(pointerEvent: SliderInputEvent.Touch) {
+        repository.update(pointerEvent)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt
index 37dbb4b..8417308 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt
@@ -16,12 +16,20 @@
 
 package com.android.systemui.volume.dialog.sliders.shared.model
 
-import android.view.MotionEvent
-
 /** Models input event happened on the Volume Slider */
 sealed interface SliderInputEvent {
 
-    data class Touch(val event: MotionEvent) : SliderInputEvent
+    interface Touch : SliderInputEvent {
+
+        val x: Float
+        val y: Float
+
+        data class Start(override val x: Float, override val y: Float) : Touch
+
+        data class Move(override val x: Float, override val y: Float) : Touch
+
+        data class End(override val x: Float, override val y: Float) : Touch
+    }
 
     data object Button : SliderInputEvent
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt
index 8109b50..38feb69 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt
@@ -20,11 +20,9 @@
 import androidx.dynamicanimation.animation.FloatValueHolder
 import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.dynamicanimation.animation.SpringForce
-import com.android.systemui.res.R
 import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
 import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel
 import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel.OverscrollEventModel
-import com.google.android.material.slider.Slider
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.launchIn
@@ -51,10 +49,6 @@
                 )
                 .addUpdateListener { _, value, _ -> viewsToAnimate.setTranslationY(value) }
 
-        view.requireViewById<Slider>(R.id.volume_dialog_slider).addOnChangeListener { s, value, _ ->
-            viewModel.setSlider(value = value, min = s.valueFrom, max = s.valueTo)
-        }
-
         viewModel.overscrollEvent
             .onEach { event ->
                 when (event) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinder.kt
deleted file mode 100644
index 5a7fbc6..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinder.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.volume.dialog.sliders.ui
-
-import android.view.View
-import com.android.systemui.haptics.slider.HapticSlider
-import com.android.systemui.haptics.slider.HapticSliderPlugin
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.util.time.SystemClock
-import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
-import com.android.systemui.volume.dialog.sliders.shared.model.SliderInputEvent
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderInputEventsViewModel
-import com.google.android.material.slider.Slider
-import com.google.android.msdl.domain.MSDLPlayer
-import javax.inject.Inject
-import kotlin.math.roundToInt
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-
-@VolumeDialogSliderScope
-class VolumeDialogSliderHapticsViewBinder
-@Inject
-constructor(
-    private val inputEventsViewModel: VolumeDialogSliderInputEventsViewModel,
-    private val vibratorHelper: VibratorHelper,
-    private val msdlPlayer: MSDLPlayer,
-    private val systemClock: SystemClock,
-) {
-
-    fun CoroutineScope.bind(view: View) {
-        val sliderView = view.requireViewById<Slider>(R.id.volume_dialog_slider)
-        val hapticSliderPlugin =
-            HapticSliderPlugin(
-                slider = HapticSlider.Slider(sliderView),
-                vibratorHelper = vibratorHelper,
-                msdlPlayer = msdlPlayer,
-                systemClock = systemClock,
-            )
-        hapticSliderPlugin.startInScope(this)
-
-        sliderView.addOnChangeListener { _, value, fromUser ->
-            hapticSliderPlugin.onProgressChanged(value.roundToInt(), fromUser)
-        }
-        sliderView.addOnSliderTouchListener(
-            object : Slider.OnSliderTouchListener {
-
-                override fun onStartTrackingTouch(slider: Slider) {
-                    hapticSliderPlugin.onStartTrackingTouch()
-                }
-
-                override fun onStopTrackingTouch(slider: Slider) {
-                    hapticSliderPlugin.onStopTrackingTouch()
-                }
-            }
-        )
-
-        inputEventsViewModel.event
-            .onEach {
-                when (it) {
-                    is SliderInputEvent.Button -> hapticSliderPlugin.onKeyDown()
-                    is SliderInputEvent.Touch -> hapticSliderPlugin.onTouchEvent(it.event)
-                }
-            }
-            .launchIn(this)
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index d403024..21a3927 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -16,96 +16,211 @@
 
 package com.android.systemui.volume.dialog.sliders.ui
 
-import android.annotation.SuppressLint
+import android.graphics.drawable.Drawable
 import android.view.View
-import androidx.dynamicanimation.animation.FloatPropertyCompat
-import androidx.dynamicanimation.animation.SpringAnimation
-import androidx.dynamicanimation.animation.SpringForce
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SliderDefaults
+import androidx.compose.material3.SliderState
+import androidx.compose.material3.VerticalSlider
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.ui.graphics.painter.DrawablePainter
+import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.res.R
 import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderInputEventsViewModel
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
+import com.android.systemui.volume.dialog.sliders.ui.compose.VolumeDialogSliderTrack
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel
 import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
-import com.google.android.material.slider.Slider
-import com.google.android.material.slider.Slider.OnSliderTouchListener
+import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider
 import javax.inject.Inject
+import kotlin.math.round
 import kotlin.math.roundToInt
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.currentCoroutineContext
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.isActive
 
 @VolumeDialogSliderScope
 class VolumeDialogSliderViewBinder
 @Inject
 constructor(
     private val viewModel: VolumeDialogSliderViewModel,
-    private val inputViewModel: VolumeDialogSliderInputEventsViewModel,
+    private val overscrollViewModel: VolumeDialogOverscrollViewModel,
+    private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
+) {
+    fun bind(view: View) {
+        val sliderComposeView: ComposeView = view.requireViewById(R.id.volume_dialog_slider)
+        sliderComposeView.setContent {
+            VolumeDialogSlider(
+                viewModel = viewModel,
+                overscrollViewModel = overscrollViewModel,
+                hapticsViewModelFactory =
+                    if (com.android.systemui.Flags.hapticsForComposeSliders()) {
+                        hapticsViewModelFactory
+                    } else {
+                        null
+                    },
+            )
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun VolumeDialogSlider(
+    viewModel: VolumeDialogSliderViewModel,
+    overscrollViewModel: VolumeDialogOverscrollViewModel,
+    hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
+    modifier: Modifier = Modifier,
 ) {
 
-    private val sliderValueProperty =
-        object : FloatPropertyCompat<Slider>("value") {
-            override fun getValue(slider: Slider): Float = slider.value
-
-            override fun setValue(slider: Slider, value: Float) {
-                slider.value = value
-            }
-        }
-    private val springForce =
-        SpringForce().apply {
-            stiffness = SpringForce.STIFFNESS_MEDIUM
-            dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
-        }
-
-    @SuppressLint("ClickableViewAccessibility")
-    fun CoroutineScope.bind(view: View) {
-        var isInitialUpdate = true
-        val sliderView: Slider = view.requireViewById(R.id.volume_dialog_slider)
-        val animation = SpringAnimation(sliderView, sliderValueProperty)
-        animation.spring = springForce
-        sliderView.setOnTouchListener { _, event ->
-            inputViewModel.onTouchEvent(event)
-            false
-        }
-        sliderView.addOnChangeListener { _, value, fromUser ->
-            viewModel.setStreamVolume(value.roundToInt(), fromUser)
-        }
-        sliderView.addOnSliderTouchListener(
-            object : OnSliderTouchListener {
-                override fun onStartTrackingTouch(slider: Slider) {}
-
-                override fun onStopTrackingTouch(slider: Slider) {
-                    viewModel.onStreamChangeFinished(slider.value.roundToInt())
-                }
-            }
+    val colors =
+        SliderDefaults.colors(
+            thumbColor = MaterialTheme.colorScheme.primary,
+            activeTickColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+            inactiveTickColor = MaterialTheme.colorScheme.primary,
+            activeTrackColor = MaterialTheme.colorScheme.primary,
+            inactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
         )
+    val collectedSliderState by viewModel.state.collectAsStateWithLifecycle(null)
+    val sliderState = collectedSliderState ?: return
 
-        viewModel.isDisabledByZenMode.onEach { sliderView.isEnabled = !it }.launchIn(this)
-        viewModel.state
-            .onEach {
-                sliderView.setModel(it, animation, isInitialUpdate)
-                isInitialUpdate = false
+    val interactionSource = remember { MutableInteractionSource() }
+    val hapticsViewModel: SliderHapticsViewModel? =
+        hapticsViewModelFactory?.let {
+            rememberViewModel(traceName = "SliderHapticsViewModel") {
+                it.create(
+                    interactionSource,
+                    sliderState.valueRange,
+                    Orientation.Vertical,
+                    VolumeHapticsConfigsProvider.sliderHapticFeedbackConfig(sliderState.valueRange),
+                    VolumeHapticsConfigsProvider.seekableSliderTrackerConfig,
+                )
             }
-            .launchIn(this)
+        }
+
+    val state =
+        remember(sliderState.valueRange) {
+            SliderState(
+                    value = sliderState.value,
+                    valueRange = sliderState.valueRange,
+                    steps =
+                        (sliderState.valueRange.endInclusive - sliderState.valueRange.start - 1)
+                            .toInt(),
+                )
+                .apply {
+                    onValueChangeFinished = {
+                        viewModel.onStreamChangeFinished(value.roundToInt())
+                        hapticsViewModel?.onValueChangeEnded()
+                    }
+                    setOnValueChangeListener {
+                        value = it
+                        hapticsViewModel?.addVelocityDataPoint(it)
+                        overscrollViewModel.setSlider(
+                            value = value,
+                            min = valueRange.start,
+                            max = valueRange.endInclusive,
+                        )
+                        viewModel.setStreamVolume(it, true)
+                    }
+                }
+        }
+    var lastDiscreteStep by remember { mutableFloatStateOf(round(sliderState.value)) }
+    LaunchedEffect(sliderState.value) {
+        state.value = sliderState.value
+        snapshotFlow { sliderState.value }
+            .map { round(it) }
+            .filter { it != lastDiscreteStep }
+            .distinctUntilChanged()
+            .collect { discreteStep ->
+                lastDiscreteStep = discreteStep
+                hapticsViewModel?.onValueChange(discreteStep)
+            }
     }
 
-    @SuppressLint("UseCompatLoadingForDrawables")
-    private fun Slider.setModel(
-        model: VolumeDialogSliderStateModel,
-        animation: SpringAnimation,
-        isInitialUpdate: Boolean,
+    VerticalSlider(
+        state = state,
+        enabled = !sliderState.isDisabled,
+        reverseDirection = true,
+        colors = colors,
+        interactionSource = interactionSource,
+        modifier =
+            modifier.pointerInput(Unit) {
+                coroutineScope {
+                    val currentContext = currentCoroutineContext()
+                    awaitPointerEventScope {
+                        while (currentContext.isActive) {
+                            viewModel.onTouchEvent(awaitPointerEvent())
+                        }
+                    }
+                }
+            },
+        track = {
+            VolumeDialogSliderTrack(
+                state,
+                colors = colors,
+                isEnabled = !sliderState.isDisabled,
+                activeTrackEndIcon = { iconsState ->
+                    VolumeIcon(sliderState.icon, iconsState.isActiveTrackEndIconVisible)
+                },
+                inactiveTrackEndIcon = { iconsState ->
+                    VolumeIcon(sliderState.icon, !iconsState.isActiveTrackEndIconVisible)
+                },
+            )
+        },
+    )
+}
+
+@Composable
+private fun BoxScope.VolumeIcon(
+    drawable: Drawable,
+    isVisible: Boolean,
+    modifier: Modifier = Modifier,
+) {
+    AnimatedVisibility(
+        visible = isVisible,
+        enter = fadeIn(animationSpec = tween(durationMillis = 50)),
+        exit = fadeOut(animationSpec = tween(durationMillis = 50)),
+        modifier = modifier.align(Alignment.Center).size(40.dp).padding(10.dp),
     ) {
-        valueFrom = model.minValue
-        animation.setMinValue(model.minValue)
-        valueTo = model.maxValue
-        animation.setMaxValue(model.maxValue)
-        // coerce the current value to the new value range before animating it. This prevents
-        // animating from the value that is outside of current [valueFrom, valueTo].
-        value = value.coerceIn(valueFrom, valueTo)
-        trackIconActiveStart = model.icon
-        if (isInitialUpdate) {
-            value = model.value
-        } else {
-            animation.animateToFinalPosition(model.value)
-        }
+        Icon(painter = DrawablePainter(drawable), contentDescription = null)
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+fun SliderState.setOnValueChangeListener(onValueChange: ((Float) -> Unit)?) {
+    with(javaClass.getDeclaredField("onValueChange")) {
+        val oldIsAccessible = isAccessible
+        AutoCloseable { isAccessible = oldIsAccessible }
+            .use {
+                isAccessible = true
+                set(this@setOnValueChangeListener, onValueChange)
+            }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index 75d427a..c66955a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -71,7 +71,6 @@
         viewsToAnimate: Array<View>,
     ) {
         with(component.sliderViewBinder()) { bind(sliderContainer) }
-        with(component.sliderHapticsViewBinder()) { bind(sliderContainer) }
         with(component.overscrollViewBinder()) { bind(sliderContainer, viewsToAnimate) }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt
new file mode 100644
index 0000000..1dd9dda
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2025 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.volume.dialog.sliders.ui.compose
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.SliderColors
+import androidx.compose.material3.SliderDefaults
+import androidx.compose.material3.SliderState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastFilter
+import androidx.compose.ui.util.fastFirst
+import kotlin.math.min
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+fun VolumeDialogSliderTrack(
+    sliderState: SliderState,
+    colors: SliderColors,
+    isEnabled: Boolean,
+    modifier: Modifier = Modifier,
+    thumbTrackGapSize: Dp = 6.dp,
+    trackCornerSize: Dp = 12.dp,
+    trackInsideCornerSize: Dp = 2.dp,
+    trackSize: Dp = 40.dp,
+    activeTrackStartIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
+    activeTrackEndIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
+    inactiveTrackStartIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
+    inactiveTrackEndIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
+) {
+    val measurePolicy = remember(sliderState) { TrackMeasurePolicy(sliderState) }
+    Layout(
+        measurePolicy = measurePolicy,
+        content = {
+            SliderDefaults.Track(
+                sliderState = sliderState,
+                colors = colors,
+                enabled = isEnabled,
+                trackCornerSize = trackCornerSize,
+                trackInsideCornerSize = trackInsideCornerSize,
+                drawStopIndicator = null,
+                thumbTrackGapSize = thumbTrackGapSize,
+                drawTick = { _, _ -> },
+                modifier = Modifier.width(trackSize).layoutId(Contents.Track),
+            )
+
+            TrackIcon(
+                icon = activeTrackStartIcon,
+                contentsId = Contents.Active.TrackStartIcon,
+                isEnabled = isEnabled,
+                colors = colors,
+                state = measurePolicy,
+            )
+            TrackIcon(
+                icon = activeTrackEndIcon,
+                contentsId = Contents.Active.TrackEndIcon,
+                isEnabled = isEnabled,
+                colors = colors,
+                state = measurePolicy,
+            )
+            TrackIcon(
+                icon = inactiveTrackStartIcon,
+                contentsId = Contents.Inactive.TrackStartIcon,
+                isEnabled = isEnabled,
+                colors = colors,
+                state = measurePolicy,
+            )
+            TrackIcon(
+                icon = inactiveTrackEndIcon,
+                contentsId = Contents.Inactive.TrackEndIcon,
+                isEnabled = isEnabled,
+                colors = colors,
+                state = measurePolicy,
+            )
+        },
+        modifier = modifier,
+    )
+}
+
+@Composable
+private fun TrackIcon(
+    icon: (@Composable BoxScope.(sliderIconsState: SliderIconsState) -> Unit)?,
+    isEnabled: Boolean,
+    contentsId: Contents,
+    state: SliderIconsState,
+    colors: SliderColors,
+    modifier: Modifier = Modifier,
+) {
+    icon ?: return
+    Box(modifier = modifier.layoutId(contentsId).fillMaxSize()) {
+        CompositionLocalProvider(
+            LocalContentColor provides contentsId.getColor(colors, isEnabled)
+        ) {
+            icon(state)
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+private class TrackMeasurePolicy(private val sliderState: SliderState) :
+    MeasurePolicy, SliderIconsState {
+
+    private val isVisible: Map<Contents, MutableState<Boolean>> =
+        mutableMapOf(
+            Contents.Active.TrackStartIcon to mutableStateOf(false),
+            Contents.Active.TrackEndIcon to mutableStateOf(false),
+            Contents.Inactive.TrackStartIcon to mutableStateOf(false),
+            Contents.Inactive.TrackEndIcon to mutableStateOf(false),
+        )
+
+    override val isActiveTrackStartIconVisible: Boolean
+        get() = isVisible.getValue(Contents.Active.TrackStartIcon).value
+
+    override val isActiveTrackEndIconVisible: Boolean
+        get() = isVisible.getValue(Contents.Active.TrackEndIcon).value
+
+    override val isInactiveTrackStartIconVisible: Boolean
+        get() = isVisible.getValue(Contents.Inactive.TrackStartIcon).value
+
+    override val isInactiveTrackEndIconVisible: Boolean
+        get() = isVisible.getValue(Contents.Inactive.TrackEndIcon).value
+
+    override fun MeasureScope.measure(
+        measurables: List<Measurable>,
+        constraints: Constraints,
+    ): MeasureResult {
+        val track = measurables.fastFirst { it.layoutId == Contents.Track }.measure(constraints)
+
+        val iconSize = min(track.width, track.height)
+        val iconConstraints = constraints.copy(maxWidth = iconSize, maxHeight = iconSize)
+
+        val icons =
+            measurables
+                .fastFilter { it.layoutId != Contents.Track }
+                .associateBy(
+                    keySelector = { it.layoutId as Contents },
+                    valueTransform = { it.measure(iconConstraints) },
+                )
+
+        return layout(track.width, track.height) {
+            with(Contents.Track) {
+                performPlacing(
+                    placeable = track,
+                    width = track.width,
+                    height = track.height,
+                    sliderState = sliderState,
+                )
+            }
+
+            for (iconLayoutId in icons.keys) {
+                with(iconLayoutId) {
+                    performPlacing(
+                        placeable = icons.getValue(iconLayoutId),
+                        width = track.width,
+                        height = track.height,
+                        sliderState = sliderState,
+                    )
+
+                    isVisible.getValue(iconLayoutId).value =
+                        isVisible(
+                            placeable = icons.getValue(iconLayoutId),
+                            width = track.width,
+                            height = track.height,
+                            sliderState = sliderState,
+                        )
+                }
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+private sealed interface Contents {
+
+    data object Track : Contents {
+        override fun Placeable.PlacementScope.performPlacing(
+            placeable: Placeable,
+            width: Int,
+            height: Int,
+            sliderState: SliderState,
+        ) = placeable.place(x = 0, y = 0)
+
+        override fun isVisible(
+            placeable: Placeable,
+            width: Int,
+            height: Int,
+            sliderState: SliderState,
+        ) = true
+
+        override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color =
+            error("Unsupported")
+    }
+
+    interface Active : Contents {
+        override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color {
+            return if (isEnabled) {
+                sliderColors.activeTickColor
+            } else {
+                sliderColors.disabledActiveTickColor
+            }
+        }
+
+        data object TrackStartIcon : Active {
+            override fun Placeable.PlacementScope.performPlacing(
+                placeable: Placeable,
+                width: Int,
+                height: Int,
+                sliderState: SliderState,
+            ) =
+                placeable.place(
+                    x = 0,
+                    y = (height * (1 - sliderState.coercedValueAsFraction)).toInt(),
+                )
+
+            override fun isVisible(
+                placeable: Placeable,
+                width: Int,
+                height: Int,
+                sliderState: SliderState,
+            ): Boolean = (height * (sliderState.coercedValueAsFraction)).toInt() > placeable.height
+        }
+
+        data object TrackEndIcon : Active {
+            override fun Placeable.PlacementScope.performPlacing(
+                placeable: Placeable,
+                width: Int,
+                height: Int,
+                sliderState: SliderState,
+            ) = placeable.place(x = 0, y = (height - placeable.height))
+
+            override fun isVisible(
+                placeable: Placeable,
+                width: Int,
+                height: Int,
+                sliderState: SliderState,
+            ): Boolean = (height * (sliderState.coercedValueAsFraction)).toInt() > placeable.height
+        }
+    }
+
+    interface Inactive : Contents {
+
+        override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color {
+            return if (isEnabled) {
+                sliderColors.inactiveTickColor
+            } else {
+                sliderColors.disabledInactiveTickColor
+            }
+        }
+
+        data object TrackStartIcon : Inactive {
+            override fun Placeable.PlacementScope.performPlacing(
+                placeable: Placeable,
+                width: Int,
+                height: Int,
+                sliderState: SliderState,
+            ) {
+                placeable.place(x = 0, y = 0)
+            }
+
+            override fun isVisible(
+                placeable: Placeable,
+                width: Int,
+                height: Int,
+                sliderState: SliderState,
+            ): Boolean =
+                (height * (1 - sliderState.coercedValueAsFraction)).toInt() > placeable.height
+        }
+
+        data object TrackEndIcon : Inactive {
+            override fun Placeable.PlacementScope.performPlacing(
+                placeable: Placeable,
+                width: Int,
+                height: Int,
+                sliderState: SliderState,
+            ) {
+                placeable.place(
+                    x = 0,
+                    y =
+                        (height * (1 - sliderState.coercedValueAsFraction)).toInt() -
+                            placeable.height,
+                )
+            }
+
+            override fun isVisible(
+                placeable: Placeable,
+                width: Int,
+                height: Int,
+                sliderState: SliderState,
+            ): Boolean =
+                (height * (1 - sliderState.coercedValueAsFraction)).toInt() > placeable.height
+        }
+    }
+
+    fun Placeable.PlacementScope.performPlacing(
+        placeable: Placeable,
+        width: Int,
+        height: Int,
+        sliderState: SliderState,
+    )
+
+    fun isVisible(placeable: Placeable, width: Int, height: Int, sliderState: SliderState): Boolean
+
+    fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color
+}
+
+/** Provides visibility state for each of the Slider's icons. */
+interface SliderIconsState {
+    val isActiveTrackStartIconVisible: Boolean
+    val isActiveTrackEndIconVisible: Boolean
+    val isInactiveTrackStartIconVisible: Boolean
+    val isInactiveTrackEndIconVisible: Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt
index 0d41860..9c97637 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt
@@ -26,7 +26,6 @@
 import javax.inject.Inject
 import kotlin.math.abs
 import kotlin.math.sign
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -38,7 +37,6 @@
 import kotlinx.coroutines.flow.transform
 
 @VolumeDialogSliderScope
-@OptIn(ExperimentalCoroutinesApi::class)
 class VolumeDialogOverscrollViewModel
 @Inject
 constructor(
@@ -95,18 +93,17 @@
     private fun overscrollEvents(direction: Float): Flow<OverscrollEventModel> {
         var startPosition: Float? = null
         return inputEventsInteractor.event
-            .mapNotNull { (it as? SliderInputEvent.Touch)?.event }
+            .mapNotNull { it as? SliderInputEvent.Touch }
             .transform { touchEvent ->
                 // Skip events from inside the slider bounds for the case when the user adjusts
-                // slider
-                // towards max when the slider is already on max value.
-                if (touchEvent.isFinalEvent()) {
+                // slider towards max when the slider is already on max value.
+                if (touchEvent is SliderInputEvent.Touch.End) {
                     startPosition = null
                     emit(OverscrollEventModel.Animate(0f))
                     return@transform
                 }
                 val currentStartPosition = startPosition
-                val newPosition: Float = touchEvent.rawY
+                val newPosition: Float = touchEvent.y
                 if (currentStartPosition == null) {
                     startPosition = newPosition
                 } else {
@@ -126,11 +123,6 @@
             }
     }
 
-    /** @return true when the [MotionEvent] indicates the end of the gesture. */
-    private fun MotionEvent.isFinalEvent(): Boolean {
-        return actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL
-    }
-
     /** Models overscroll event */
     sealed interface OverscrollEventModel {
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModel.kt
deleted file mode 100644
index 755776ac..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModel.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.volume.dialog.sliders.ui.viewmodel
-
-import android.view.MotionEvent
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
-import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
-import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInputEventsInteractor
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.stateIn
-
-@VolumeDialogSliderScope
-class VolumeDialogSliderInputEventsViewModel
-@Inject
-constructor(
-    @VolumeDialog private val coroutineScope: CoroutineScope,
-    private val interactor: VolumeDialogSliderInputEventsInteractor,
-) {
-
-    val event =
-        interactor.event.stateIn(coroutineScope, SharingStarted.Eagerly, null).filterNotNull()
-
-    fun onTouchEvent(event: MotionEvent) {
-        interactor.onTouchEvent(event)
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
index 8df9e78..b01046b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
@@ -20,17 +20,20 @@
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
 
 data class VolumeDialogSliderStateModel(
-    val minValue: Float,
-    val maxValue: Float,
     val value: Float,
+    val isDisabled: Boolean,
+    val valueRange: ClosedFloatingPointRange<Float>,
     val icon: Drawable,
 )
 
-fun VolumeDialogStreamModel.toStateModel(icon: Drawable): VolumeDialogSliderStateModel {
+fun VolumeDialogStreamModel.toStateModel(
+    isDisabled: Boolean,
+    icon: Drawable,
+): VolumeDialogSliderStateModel {
     return VolumeDialogSliderStateModel(
-        minValue = levelMin.toFloat(),
         value = level.toFloat(),
-        maxValue = levelMax.toFloat(),
+        isDisabled = isDisabled,
+        valueRange = levelMin.toFloat()..levelMax.toFloat(),
         icon = icon,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index a752f1f..9ac052a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.volume.dialog.sliders.ui.viewmodel
 
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEvent
+import androidx.compose.ui.input.pointer.PointerEventType
 import com.android.systemui.util.time.SystemClock
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
@@ -23,20 +26,22 @@
 import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
 import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
+import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInputEventsInteractor
 import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
 import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
+import com.android.systemui.volume.dialog.sliders.shared.model.SliderInputEvent
 import javax.inject.Inject
+import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
 
@@ -52,7 +57,6 @@
 // TODO(b/375355785) remove this
 private const val VOLUME_UPDATE_GRACE_PERIOD = 1000
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @VolumeDialogSliderScope
 class VolumeDialogSliderViewModel
 @Inject
@@ -62,6 +66,7 @@
     private val visibilityInteractor: VolumeDialogVisibilityInteractor,
     @VolumeDialog private val coroutineScope: CoroutineScope,
     private val volumeDialogSliderIconProvider: VolumeDialogSliderIconProvider,
+    private val inputEventsInteractor: VolumeDialogSliderInputEventsInteractor,
     private val systemClock: SystemClock,
     private val logger: VolumeDialogLogger,
 ) {
@@ -77,11 +82,12 @@
             .stateIn(coroutineScope, SharingStarted.Eagerly, null)
             .filterNotNull()
 
-    val isDisabledByZenMode: Flow<Boolean> = interactor.isDisabledByZenMode
     val state: Flow<VolumeDialogSliderStateModel> =
-        model
-            .flatMapLatest { streamModel ->
-                with(streamModel) {
+        combine(
+                interactor.isDisabledByZenMode,
+                model,
+                model.flatMapLatest { streamModel ->
+                    with(streamModel) {
                         val isMuted = muteSupported && muted
                         when (sliderType) {
                             is VolumeDialogSliderType.Stream ->
@@ -101,7 +107,9 @@
                             }
                         }
                     }
-                    .map { icon -> streamModel.toStateModel(icon) }
+                },
+            ) { isDisabledByZenMode, model, icon ->
+                model.toStateModel(icon = icon, isDisabled = isDisabledByZenMode)
             }
             .stateIn(coroutineScope, SharingStarted.Eagerly, null)
             .filterNotNull()
@@ -116,11 +124,14 @@
             .launchIn(coroutineScope)
     }
 
-    fun setStreamVolume(volume: Int, fromUser: Boolean) {
+    fun setStreamVolume(volume: Float, fromUser: Boolean) {
         if (fromUser) {
             visibilityInteractor.resetDismissTimeout()
             userVolumeUpdates.value =
-                VolumeUpdate(newVolumeLevel = volume, timestampMillis = getTimestampMillis())
+                VolumeUpdate(
+                    newVolumeLevel = volume.roundToInt(),
+                    timestampMillis = getTimestampMillis(),
+                )
         }
     }
 
@@ -128,6 +139,28 @@
         logger.onVolumeSliderAdjustmentFinished(volume = volume, stream = sliderType.audioStream)
     }
 
+    fun onTouchEvent(pointerEvent: PointerEvent) {
+        val position: Offset = pointerEvent.changes.first().position
+        when (pointerEvent.type) {
+            PointerEventType.Press ->
+                inputEventsInteractor.onTouchEvent(
+                    SliderInputEvent.Touch.Start(position.x, position.y)
+                )
+            PointerEventType.Move ->
+                inputEventsInteractor.onTouchEvent(
+                    SliderInputEvent.Touch.Move(position.x, position.y)
+                )
+            PointerEventType.Scroll ->
+                inputEventsInteractor.onTouchEvent(
+                    SliderInputEvent.Touch.Move(position.x, position.y)
+                )
+            PointerEventType.Release ->
+                inputEventsInteractor.onTouchEvent(
+                    SliderInputEvent.Touch.End(position.x, position.y)
+                )
+        }
+    }
+
     private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
 
     private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index 428dc6e..b8c0fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -45,7 +45,6 @@
 import com.android.systemui.volume.dialog.utils.VolumeTracer
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.launchIn
@@ -56,7 +55,6 @@
 import kotlinx.coroutines.suspendCancellableCoroutine
 
 /** Binds the root view of the Volume Dialog. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @VolumeDialogScope
 class VolumeDialogViewBinder
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index 9bab1b0..e7a2cf4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -27,12 +27,10 @@
 import javax.inject.Inject
 import javax.inject.Provider
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @VolumeDialogPluginScope
 class VolumeDialogPluginViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
index 73728e6..4dbdba1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
@@ -35,7 +35,6 @@
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -45,7 +44,6 @@
 
 /** Provides a currently active audio device data. */
 @VolumePanelScope
-@OptIn(ExperimentalCoroutinesApi::class)
 class AudioOutputInteractor
 @Inject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.kt
new file mode 100644
index 0000000..92e9bf2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025 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.volume.haptics.ui
+
+import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
+import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
+
+object VolumeHapticsConfigsProvider {
+
+    fun sliderHapticFeedbackConfig(
+        valueRange: ClosedFloatingPointRange<Float>
+    ): SliderHapticFeedbackConfig {
+        val sliderStepSize = 1f / (valueRange.endInclusive - valueRange.start)
+        return SliderHapticFeedbackConfig(
+            lowerBookendScale = 0.2f,
+            progressBasedDragMinScale = 0.2f,
+            progressBasedDragMaxScale = 0.5f,
+            deltaProgressForDragThreshold = 0f,
+            additionalVelocityMaxBump = 0.2f,
+            maxVelocityToScale = 0.1f, /* slider progress(from 0 to 1) per sec */
+            sliderStepSize = sliderStepSize,
+        )
+    }
+
+    val seekableSliderTrackerConfig =
+        SeekableSliderTrackerConfig(lowerBookendThreshold = 0f, upperBookendThreshold = 1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
index cfff457..e95a09a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -38,7 +37,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** Provides a valid slice from [AncSliceRepository]. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @VolumePanelScope
 class AncSliceInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
index c980eb4..7d81134 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -34,7 +33,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** Volume Panel ANC component view model. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @VolumePanelScope
 class AncViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
index 12e624ca..8253872 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.FlowCollector
 import kotlinx.coroutines.flow.filterIsInstance
@@ -37,7 +36,6 @@
 import kotlinx.coroutines.withContext
 
 /** Allows to observe and change [MediaDeviceSession] state. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MediaDeviceSessionInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
index 609ba02..fbb544e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.volume.panel.shared.model.wrapInResult
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -44,7 +43,6 @@
 import kotlinx.coroutines.flow.stateIn
 
 /** Gathers together a domain state for the Media Output Volume Panel component. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @VolumePanelScope
 class MediaOutputComponentInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index 2973e11..1bb186b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -37,7 +37,6 @@
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -54,7 +53,6 @@
 import kotlinx.coroutines.withContext
 
 /** Provides observable models about the current media session state. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MediaOutputInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index 6c4a853..f6edd47 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -34,14 +34,12 @@
 import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /** Models the UI of the Media Output Volume Panel component. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @VolumePanelScope
 class MediaOutputViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
index 4ce9fe5..5f20b4e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
@@ -29,7 +29,6 @@
 import dagger.assisted.AssistedInject
 import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -39,7 +38,6 @@
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
-@OptIn(ExperimentalCoroutinesApi::class)
 class AudioSharingStreamSliderViewModel
 @AssistedInject
 constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
index 28f1105..0813eac 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.volume.panel.shared.model.filterData
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -52,7 +51,6 @@
  * Controls the behaviour of the whole audio
  * [com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent].
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @VolumePanelScope
 class AudioVolumeComponentViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
index 2f60c4b..f52693a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -42,7 +42,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
@@ -56,7 +55,6 @@
 
 // Can't inject a constructor here because VolumePanelComponent provides this view model for its
 // components.
-@OptIn(ExperimentalCoroutinesApi::class)
 class VolumePanelViewModel(
     resources: Resources,
     coroutineScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
index e590a7de..680cf76 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
@@ -40,7 +40,6 @@
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.emptyFlow
@@ -49,7 +48,6 @@
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class VolumeNavigator
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
index af84e4e..594c552 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.flags.Flags
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -45,7 +44,6 @@
 import kotlinx.coroutines.flow.update
 import com.android.app.tracing.coroutines.launchTraced as launch
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class WalletContextualSuggestionsController
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt
index c1fb0e8..33e1929 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt
@@ -16,17 +16,30 @@
 
 package com.android.systemui.wallpapers
 
+import android.app.Flags
 import android.graphics.Canvas
 import android.graphics.Paint
+import android.graphics.RadialGradient
+import android.graphics.Shader
 import android.service.wallpaper.WallpaperService
 import android.util.Log
 import android.view.SurfaceHolder
+import androidx.core.graphics.ColorUtils
 import androidx.core.graphics.toRectF
+import com.android.systemui.res.R
 
 /** A wallpaper that shows a static gradient color image wallpaper. */
 class GradientColorWallpaper : WallpaperService() {
 
-    override fun onCreateEngine(): Engine = GradientColorWallpaperEngine()
+    override fun onCreateEngine(): Engine =
+        if (Flags.enableConnectedDisplaysWallpaper()) {
+            GradientColorWallpaperEngine()
+        } else {
+            EmptyWallpaperEngine()
+        }
+
+    /** Empty engine used when the feature flag is disabled. */
+    inner class EmptyWallpaperEngine : Engine()
 
     inner class GradientColorWallpaperEngine : Engine() {
         init {
@@ -45,9 +58,60 @@
                 canvas = surface.lockHardwareCanvas()
                 val destRectF = surfaceHolder.surfaceFrame.toRectF()
                 val toColor = context.getColor(com.android.internal.R.color.materialColorPrimary)
+                val fromColor =
+                    ColorUtils.setAlphaComponent(
+                        context.getColor(
+                            com.android.internal.R.color.materialColorPrimaryContainer
+                        ),
+                        /* alpha= */ 153, // 0.6f * 255
+                    )
 
-                // TODO(b/384519696): Draw the actual gradient color wallpaper instead.
                 canvas.drawRect(destRectF, Paint().apply { color = toColor })
+
+                val offsetPx: Float =
+                    context.resources
+                        .getDimensionPixelSize(R.dimen.gradient_color_wallpaper_center_offset)
+                        .toFloat()
+                val totalHeight = destRectF.height() + (offsetPx * 2)
+                val leftCenterX = -offsetPx
+                val leftCenterY = -offsetPx
+                val rightCenterX = offsetPx + destRectF.width()
+                val rightCenterY = totalHeight - offsetPx
+                val radius = (destRectF.width() / 2) + offsetPx
+
+                canvas.drawCircle(
+                    leftCenterX,
+                    leftCenterY,
+                    radius,
+                    Paint().apply {
+                        shader =
+                            RadialGradient(
+                                /* centerX= */ leftCenterX,
+                                /* centerY= */ leftCenterY,
+                                /* radius= */ radius,
+                                /* centerColor= */ fromColor,
+                                /* edgeColor= */ toColor,
+                                /* tileMode= */ Shader.TileMode.CLAMP,
+                            )
+                    },
+                )
+
+                canvas.drawCircle(
+                    rightCenterX,
+                    rightCenterY,
+                    radius,
+                    Paint().apply {
+                        shader =
+                            RadialGradient(
+                                /* centerX= */ rightCenterX,
+                                /* centerY= */ rightCenterY,
+                                /* radius= */ radius,
+                                /* centerColor= */ fromColor,
+                                /* edgeColor= */ toColor,
+                                /* tileMode= */ Shader.TileMode.CLAMP,
+                            )
+                    },
+                )
             } catch (exception: IllegalStateException) {
                 Log.d(TAG, "Fail to draw in the canvas", exception)
             } finally {
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
index 8487ee7..ec74f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -36,4 +36,5 @@
     override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow()
     override val wallpaperSupportsAmbientMode = flowOf(false)
     override var rootView: View? = null
+    override val shouldSendFocalArea: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index ed43f83..9794c61 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.shared.Flags.ambientAod
 import com.android.systemui.user.data.model.SelectedUserModel
@@ -45,6 +44,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
@@ -65,6 +65,9 @@
 
     /** Set rootView to get its windowToken afterwards */
     var rootView: View?
+
+    /** when we use magic portrait wallpapers, we should always get its bounds from keyguard */
+    val shouldSendFocalArea: StateFlow<Boolean>
 }
 
 @SysUISingleton
@@ -76,7 +79,6 @@
     broadcastDispatcher: BroadcastDispatcher,
     userRepository: UserRepository,
     keyguardRepository: KeyguardRepository,
-    keyguardClockRepository: KeyguardClockRepository,
     private val wallpaperManager: WallpaperManager,
     context: Context,
 ) : WallpaperRepository {
@@ -97,27 +99,7 @@
             // Only update the wallpaper status once the user selection has finished.
             .filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
 
-    /** The bottom of notification stack respect to the top of screen. */
-    private val notificationStackAbsoluteBottom: StateFlow<Float> =
-        keyguardRepository.notificationStackAbsoluteBottom
-
-    /** The top of shortcut respect to the top of screen. */
-    private val shortcutAbsoluteTop: StateFlow<Float> = keyguardRepository.shortcutAbsoluteTop
-
-    /**
-     * The top of notification stack to give a default state of lockscreen remaining space for
-     * states with notifications to compare with. It's the bottom of smartspace date and weather
-     * smartspace in small clock state, plus proper bottom margin.
-     */
-    private val notificationStackDefaultTop = keyguardClockRepository.notificationDefaultTop
     @VisibleForTesting var sendLockscreenLayoutJob: Job? = null
-    private val lockscreenRemainingSpaceWithNotification: Flow<Triple<Float, Float, Float>> =
-        combine(
-            notificationStackAbsoluteBottom,
-            notificationStackDefaultTop,
-            shortcutAbsoluteTop,
-            ::Triple,
-        )
 
     override val wallpaperInfo: StateFlow<WallpaperInfo?> =
         if (!wallpaperManager.isWallpaperSupported) {
@@ -140,15 +122,16 @@
 
     override var rootView: View? = null
 
-    val shouldSendNotificationLayout =
+    override val shouldSendFocalArea =
         wallpaperInfo
             .map {
-                val shouldSendNotificationLayout = shouldSendNotificationLayout(it)
+                val shouldSendNotificationLayout =
+                    it?.component?.className == MAGIC_PORTRAIT_CLASSNAME
                 if (shouldSendNotificationLayout) {
                     sendLockscreenLayoutJob =
                         scope.launch {
-                            lockscreenRemainingSpaceWithNotification.collect {
-                                (notificationBottom, notificationDefaultTop, shortcutTop) ->
+                            keyguardRepository.wallpaperFocalAreaBounds.collect {
+                                wallpaperFocalAreaBounds ->
                                 wallpaperManager.sendWallpaperCommand(
                                     /* windowToken = */ rootView?.windowToken,
                                     /* action = */ WallpaperManager
@@ -157,14 +140,22 @@
                                     /* y = */ 0,
                                     /* z = */ 0,
                                     /* extras = */ Bundle().apply {
-                                        putFloat("screenLeft", 0F)
-                                        putFloat("smartspaceBottom", notificationDefaultTop)
-                                        putFloat("notificationBottom", notificationBottom)
                                         putFloat(
-                                            "screenRight",
-                                            context.resources.displayMetrics.widthPixels.toFloat(),
+                                            "wallpaperFocalAreaLeft",
+                                            wallpaperFocalAreaBounds.left,
                                         )
-                                        putFloat("shortCutTop", shortcutTop)
+                                        putFloat(
+                                            "wallpaperFocalAreaRight",
+                                            wallpaperFocalAreaBounds.right,
+                                        )
+                                        putFloat(
+                                            "wallpaperFocalAreaTop",
+                                            wallpaperFocalAreaBounds.top,
+                                        )
+                                        putFloat(
+                                            "wallpaperFocalAreaBottom",
+                                            wallpaperFocalAreaBounds.bottom,
+                                        )
                                     },
                                 )
                             }
@@ -176,10 +167,9 @@
             }
             .stateIn(
                 scope,
-                // Always be listening for wallpaper changes.
-                if (Flags.magicPortraitWallpapers()) SharingStarted.Eagerly
-                else SharingStarted.Lazily,
-                initialValue = false,
+                // Always be listening for wallpaper changes when magic portrait flag is on
+                if (Flags.magicPortraitWallpapers()) SharingStarted.Eagerly else WhileSubscribed(),
+                initialValue = Flags.magicPortraitWallpapers(),
             )
 
     private suspend fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? {
@@ -188,14 +178,6 @@
         }
     }
 
-    private fun shouldSendNotificationLayout(wallpaperInfo: WallpaperInfo?): Boolean {
-        return if (wallpaperInfo != null && wallpaperInfo.component != null) {
-            wallpaperInfo.component!!.className == MAGIC_PORTRAIT_CLASSNAME
-        } else {
-            false
-        }
-    }
-
     companion object {
         const val MAGIC_PORTRAIT_CLASSNAME =
             "com.google.android.apps.magicportrait.service.MagicPortraitWallpaperService"
diff --git a/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt b/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt
index 8e0616c..bfa3498 100644
--- a/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt
@@ -18,6 +18,7 @@
 
 import android.util.Log
 import com.android.systemui.Flags
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -46,6 +47,7 @@
     @Application private val applicationScope: CoroutineScope,
     private val keyguardInteractor: KeyguardInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    private val communalInteractor: CommunalInteractor,
     private val repository: WindowRootViewBlurRepository,
 ) {
     private var isBouncerTransitionInProgress: StateFlow<Boolean> =
@@ -87,6 +89,23 @@
     }
 
     /**
+     * Request to apply blur while on glanceable hub, this takes precedence over other blurs (from
+     * shade) except for bouncer.
+     */
+    fun requestBlurForGlanceableHub(blurRadius: Int): Boolean {
+        if (keyguardInteractor.primaryBouncerShowing.value) {
+            return false
+        }
+
+        Log.d(TAG, "requestBlurForGlanceableHub for $blurRadius")
+
+        repository.isBlurOpaque.value = false
+        repository.blurRadius.value = blurRadius
+
+        return true
+    }
+
+    /**
      * Method that requests blur to be applied on window root view. It is applied only when other
      * blurs are not applied.
      *
@@ -103,6 +122,9 @@
         if (keyguardInteractor.primaryBouncerShowing.value || isBouncerTransitionInProgress.value) {
             return false
         }
+        if (communalInteractor.isCommunalBlurring.value) {
+            return false
+        }
         Log.d(TAG, "requestingBlurForShade for $blurRadius $opaque")
         repository.blurRadius.value = blurRadius
         repository.isBlurOpaque.value = opaque
diff --git a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
index dbccc1d..e09a74c 100644
--- a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.scene.ui.view.WindowRootView
 import com.android.systemui.statusbar.BlurUtils
 import com.android.systemui.window.ui.viewmodel.WindowRootViewModel
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.launch
@@ -42,11 +43,12 @@
         viewModelFactory: WindowRootViewModel.Factory,
         blurUtils: BlurUtils?,
         choreographer: Choreographer?,
+        mainDispatcher: CoroutineDispatcher,
     ) {
-        if (!Flags.bouncerUiRevamp()) return
+        if (!Flags.bouncerUiRevamp() && !Flags.glanceableHubBlurredBackground()) return
         if (blurUtils == null || choreographer == null) return
 
-        view.repeatWhenAttached {
+        view.repeatWhenAttached(mainDispatcher) {
             Log.d(TAG, "Binding root view")
             var frameCallbackPendingExecution: FrameCallback? = null
             view.viewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt
index 199d02d..72cca75 100644
--- a/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt
@@ -19,6 +19,8 @@
 import android.os.Build
 import android.util.Log
 import com.android.app.tracing.coroutines.launchTraced
+import com.android.systemui.Flags.glanceableHubBlurredBackground
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
@@ -40,6 +42,7 @@
 @AssistedInject
 constructor(
     private val primaryBouncerTransitions: Set<@JvmSuppressWildcards PrimaryBouncerTransition>,
+    private val glanceableHubTransitions: Set<@JvmSuppressWildcards GlanceableHubTransition>,
     private val blurInteractor: WindowRootViewBlurInteractor,
 ) : ExclusiveActivatable() {
 
@@ -80,6 +83,26 @@
                         blurInteractor.requestBlurForBouncer(blurRadius.toInt())
                     }
             }
+
+            if (glanceableHubBlurredBackground()) {
+                launchTraced("WindowRootViewModel#glanceableHubTransitions") {
+                    glanceableHubTransitions
+                        .map { transition ->
+                            transition.windowBlurRadius.onEach { blurRadius ->
+                                if (isLoggable) {
+                                    Log.d(
+                                        TAG,
+                                        "${transition.javaClass.simpleName} windowBlurRadius $blurRadius",
+                                    )
+                                }
+                            }
+                        }
+                        .merge()
+                        .collect { blurRadius ->
+                            blurInteractor.requestBlurForGlanceableHub(blurRadius.toInt())
+                        }
+                }
+            }
         }
         awaitCancellation()
     }
diff --git a/packages/SystemUI/src/com/google/android/systemui/lowlightclock/LowLightClockDreamService.java b/packages/SystemUI/src/com/google/android/systemui/lowlightclock/LowLightClockDreamService.java
new file mode 100644
index 0000000..8a5f7ea
--- /dev/null
+++ b/packages/SystemUI/src/com/google/android/systemui/lowlightclock/LowLightClockDreamService.java
@@ -0,0 +1,161 @@
+/*
+ * 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.google.android.systemui.lowlightclock;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.Nullable;
+import android.service.dreams.DreamService;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextClock;
+import android.widget.TextView;
+
+import com.android.dream.lowlight.LowLightTransitionCoordinator;
+import com.android.systemui.lowlightclock.ChargingStatusProvider;
+import com.android.systemui.lowlightclock.LowLightClockAnimationProvider;
+import com.android.systemui.lowlightclock.LowLightDisplayController;
+import com.android.systemui.res.R;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/**
+ * A dark themed text clock dream to be shown when the device is in a low light environment.
+ */
+public class LowLightClockDreamService extends DreamService implements
+        LowLightTransitionCoordinator.LowLightExitListener {
+    private static final String TAG = "LowLightClockDreamService";
+
+    private final ChargingStatusProvider mChargingStatusProvider;
+    private final LowLightDisplayController mDisplayController;
+    private final LowLightClockAnimationProvider mAnimationProvider;
+    private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
+    private boolean mIsDimBrightnessSupported = false;
+
+    private TextView mChargingStatusTextView;
+    private TextClock mTextClock;
+    @Nullable
+    private Animator mAnimationIn;
+    @Nullable
+    private Animator mAnimationOut;
+
+    @Inject
+    public LowLightClockDreamService(
+            ChargingStatusProvider chargingStatusProvider,
+            LowLightClockAnimationProvider animationProvider,
+            LowLightTransitionCoordinator lowLightTransitionCoordinator,
+            Optional<Provider<LowLightDisplayController>> displayController) {
+        super();
+
+        mAnimationProvider = animationProvider;
+        mDisplayController = displayController.map(Provider::get).orElse(null);
+        mChargingStatusProvider = chargingStatusProvider;
+        mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        setInteractive(false);
+        setFullscreen(true);
+
+        setContentView(LayoutInflater.from(getApplicationContext()).inflate(
+                R.layout.low_light_clock_dream, null));
+
+        mTextClock = findViewById(R.id.low_light_text_clock);
+
+        mChargingStatusTextView = findViewById(R.id.charging_status_text_view);
+
+        mChargingStatusProvider.startUsing(this::updateChargingMessage);
+
+        mLowLightTransitionCoordinator.setLowLightExitListener(this);
+    }
+
+    @Override
+    public void onDreamingStarted() {
+        mAnimationIn = mAnimationProvider.provideAnimationIn(mTextClock, mChargingStatusTextView);
+        mAnimationIn.start();
+
+        if (mDisplayController != null) {
+            mIsDimBrightnessSupported = mDisplayController.isDisplayBrightnessModeSupported();
+
+            if (mIsDimBrightnessSupported) {
+                Log.v(TAG, "setting dim brightness state");
+                mDisplayController.setDisplayBrightnessModeEnabled(true);
+            } else {
+                Log.v(TAG, "dim brightness not supported");
+            }
+        }
+    }
+
+    @Override
+    public void onDreamingStopped() {
+        if (mIsDimBrightnessSupported) {
+            Log.v(TAG, "clearing dim brightness state");
+            mDisplayController.setDisplayBrightnessModeEnabled(false);
+        }
+    }
+
+    @Override
+    public void onWakeUp() {
+        if (mAnimationIn != null) {
+            mAnimationIn.cancel();
+        }
+        mAnimationOut = mAnimationProvider.provideAnimationOut(mTextClock, mChargingStatusTextView);
+        mAnimationOut.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                LowLightClockDreamService.super.onWakeUp();
+            }
+        });
+        mAnimationOut.start();
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        if (mAnimationOut != null) {
+            mAnimationOut.cancel();
+        }
+
+        mChargingStatusProvider.stopUsing();
+
+        mLowLightTransitionCoordinator.setLowLightExitListener(null);
+    }
+
+    private void updateChargingMessage(boolean showChargingStatus, String chargingStatusMessage) {
+        mChargingStatusTextView.setText(chargingStatusMessage);
+        mChargingStatusTextView.setVisibility(showChargingStatus ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    @Override
+    public Animator onBeforeExitLowLight() {
+        mAnimationOut = mAnimationProvider.provideAnimationOut(mTextClock, mChargingStatusTextView);
+        mAnimationOut.start();
+
+        // Return the animator so that the transition coordinator waits for the low light exit
+        // animations to finish before entering low light, as otherwise the default DreamActivity
+        // animation plays immediately and there's no time for this animation to play.
+        return mAnimationOut;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index 057ddcd..8bfd254 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -21,7 +21,7 @@
 
 import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent;
 import static com.android.systemui.accessibility.WindowMagnificationSettings.MagnificationSize;
-import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.recents.LauncherProxyService.LauncherProxyListener;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -53,7 +53,7 @@
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.util.settings.SecureSettings;
@@ -80,13 +80,13 @@
     @Mock
     private IMagnificationConnectionCallback mConnectionCallback;
     @Mock
-    private OverviewProxyService mOverviewProxyService;
+    private LauncherProxyService mLauncherProxyService;
     @Mock
     private SecureSettings mSecureSettings;
 
     private CommandQueue mCommandQueue;
     private MagnificationImpl mMagnification;
-    private OverviewProxyListener mOverviewProxyListener;
+    private LauncherProxyListener mLauncherProxyListener;
     private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
 
     @Mock
@@ -130,7 +130,7 @@
         mMagnification = new MagnificationImpl(getContext(),
                 getContext().getMainThreadHandler(), mContext.getMainExecutor(),
                 mCommandQueue, mModeSwitchesController,
-                mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker,
+                mSysUiState, mLauncherProxyService, mSecureSettings, mDisplayTracker,
                 getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager,
                 getContext().getSystemService(AccessibilityManager.class),
                 mViewCaptureAwareWindowManager);
@@ -140,10 +140,10 @@
                 mContext.getSystemService(DisplayManager.class), mMagnificationSettingsController);
         mMagnification.start();
 
-        final ArgumentCaptor<OverviewProxyListener> listenerArgumentCaptor =
-                ArgumentCaptor.forClass(OverviewProxyListener.class);
-        verify(mOverviewProxyService).addCallback(listenerArgumentCaptor.capture());
-        mOverviewProxyListener = listenerArgumentCaptor.getValue();
+        final ArgumentCaptor<LauncherProxyListener> listenerArgumentCaptor =
+                ArgumentCaptor.forClass(LauncherProxyListener.class);
+        verify(mLauncherProxyService).addCallback(listenerArgumentCaptor.capture());
+        mLauncherProxyListener = listenerArgumentCaptor.getValue();
     }
 
     @Test
@@ -336,7 +336,7 @@
 
     @Test
     public void overviewProxyIsConnected_noController_resetFlag() {
-        mOverviewProxyListener.onConnectionChanged(true);
+        mLauncherProxyListener.onConnectionChanged(true);
 
         verify(mSysUiState).setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false);
         verify(mSysUiState).commitUpdate(mContext.getDisplayId());
@@ -349,7 +349,7 @@
                 mContext.getSystemService(DisplayManager.class), mController);
         mMagnification.mWindowMagnificationControllerSupplier.get(TEST_DISPLAY);
 
-        mOverviewProxyListener.onConnectionChanged(true);
+        mLauncherProxyListener.onConnectionChanged(true);
 
         verify(mController).updateSysUIStateFlag();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 21519b0..ab691c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -304,9 +304,7 @@
         testScope = TestScope(testDispatcher)
         underTest =
             KeyguardQuickAffordanceInteractor(
-                keyguardInteractor =
-                    KeyguardInteractorFactory.create(featureFlags = featureFlags)
-                        .keyguardInteractor,
+                keyguardInteractor = kosmos.keyguardInteractor,
                 shadeInteractor = kosmos.shadeInteractor,
                 lockPatternUtils = lockPatternUtils,
                 keyguardStateController = keyguardStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index 222a7fe..4189720 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -81,6 +81,7 @@
                 { falsingManager },
                 { mock(VibratorHelper::class.java) },
                 logcatLogBuffer(),
+                logcatLogBuffer("blueprints"),
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index b5a2271..051aba3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -44,9 +44,10 @@
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -191,9 +192,8 @@
         dockManager = DockManagerFake()
         biometricSettingsRepository = FakeBiometricSettingsRepository()
 
-        val withDeps = KeyguardInteractorFactory.create()
-        keyguardInteractor = withDeps.keyguardInteractor
-        repository = withDeps.repository
+        keyguardInteractor = kosmos.keyguardInteractor
+        repository = kosmos.fakeKeyguardRepository
 
         whenever(userTracker.userHandle).thenReturn(mock())
         whenever(lockPatternUtils.getStrongAuthForUser(ArgumentMatchers.anyInt()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt
new file mode 100644
index 0000000..43ee388e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.lowlightclock
+
+import android.hardware.Sensor
+import android.hardware.SensorEventListener
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.sensors.AsyncSensorManager
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class AmbientLightModeMonitorTest : SysuiTestCase() {
+    @Mock private lateinit var sensorManager: AsyncSensorManager
+    @Mock private lateinit var sensor: Sensor
+    @Mock private lateinit var algorithm: AmbientLightModeMonitor.DebounceAlgorithm
+
+    private lateinit var ambientLightModeMonitor: AmbientLightModeMonitor
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        ambientLightModeMonitor =
+            AmbientLightModeMonitor(Optional.of(algorithm), sensorManager, Optional.of(sensor))
+    }
+
+    @Test
+    fun shouldRegisterSensorEventListenerOnStart() {
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        ambientLightModeMonitor.start(callback)
+
+        verify(sensorManager).registerListener(any(), eq(sensor), anyInt())
+    }
+
+    @Test
+    fun shouldUnregisterSensorEventListenerOnStop() {
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        ambientLightModeMonitor.start(callback)
+
+        val sensorEventListener = captureSensorEventListener()
+
+        ambientLightModeMonitor.stop()
+
+        verify(sensorManager).unregisterListener(eq(sensorEventListener))
+    }
+
+    @Test
+    fun shouldStartDebounceAlgorithmOnStart() {
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        ambientLightModeMonitor.start(callback)
+
+        verify(algorithm).start(eq(callback))
+    }
+
+    @Test
+    fun shouldStopDebounceAlgorithmOnStop() {
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        ambientLightModeMonitor.start(callback)
+        ambientLightModeMonitor.stop()
+
+        verify(algorithm).stop()
+    }
+
+    @Test
+    fun shouldNotRegisterForSensorUpdatesIfSensorNotAvailable() {
+        val ambientLightModeMonitor =
+            AmbientLightModeMonitor(Optional.of(algorithm), sensorManager, Optional.empty())
+
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        ambientLightModeMonitor.start(callback)
+
+        verify(sensorManager, never()).registerListener(any(), any(Sensor::class.java), anyInt())
+    }
+
+    // Captures [SensorEventListener], assuming it has been registered with [sensorManager].
+    private fun captureSensorEventListener(): SensorEventListener {
+        val captor = ArgumentCaptor.forClass(SensorEventListener::class.java)
+        verify(sensorManager).registerListener(captor.capture(), any(), anyInt())
+        return captor.value
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ChargingStatusProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ChargingStatusProviderTest.java
new file mode 100644
index 0000000..2c8c1e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ChargingStatusProviderTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.lowlightclock;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.BatteryManager;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ChargingStatusProviderTest extends SysuiTestCase {
+    @Mock
+    private Resources mResources;
+    @Mock
+    private IBatteryStats mBatteryInfo;
+    @Mock
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    private ChargingStatusProvider.Callback mCallback;
+
+    private ChargingStatusProvider mProvider;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        mProvider = new ChargingStatusProvider(
+                mContext, mResources, mBatteryInfo, mKeyguardUpdateMonitor);
+    }
+
+    @Test
+    public void testStartUsingReportsStatusToCallback() {
+        mProvider.startUsing(mCallback);
+        verify(mCallback).onChargingStatusChanged(false, null);
+    }
+
+    @Test
+    public void testStartUsingRegistersCallbackWithKeyguardUpdateMonitor() {
+        mProvider.startUsing(mCallback);
+        verify(mKeyguardUpdateMonitor).registerCallback(any());
+    }
+
+    @Test
+    public void testCallbackNotCalledAfterStopUsing() {
+        mProvider.startUsing(mCallback);
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+        verify(mKeyguardUpdateMonitor)
+                .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+        mProvider.stopUsing();
+        keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+                .onRefreshBatteryInfo(getChargingBattery());
+        verify(mCallback, never()).onChargingStatusChanged(eq(true), any());
+    }
+
+    @Test
+    public void testKeyguardUpdateMonitorCallbackRemovedAfterStopUsing() {
+        mProvider.startUsing(mCallback);
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+        verify(mKeyguardUpdateMonitor)
+                .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+        mProvider.stopUsing();
+        verify(mKeyguardUpdateMonitor)
+                .removeCallback(keyguardUpdateMonitorCallbackArgumentCaptor.getValue());
+    }
+
+    @Test
+    public void testChargingStatusReportsHideWhenNotPluggedIn() {
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+        mProvider.startUsing(mCallback);
+        verify(mKeyguardUpdateMonitor)
+                .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+        keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+                .onRefreshBatteryInfo(getUnpluggedBattery());
+        // Once for init() and once for the status change.
+        verify(mCallback, times(2)).onChargingStatusChanged(false, null);
+    }
+
+    @Test
+    public void testChargingStatusReportsShowWhenBatteryOverheated() {
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+        mProvider.startUsing(mCallback);
+        verify(mCallback).onChargingStatusChanged(false, null);
+        verify(mKeyguardUpdateMonitor)
+                .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+        keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+                .onRefreshBatteryInfo(getBatteryDefender());
+        verify(mCallback).onChargingStatusChanged(eq(true), any());
+    }
+
+    @Test
+    public void testChargingStatusReportsShowWhenPluggedIn() {
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+        mProvider.startUsing(mCallback);
+        verify(mCallback).onChargingStatusChanged(false, null);
+        verify(mKeyguardUpdateMonitor)
+                .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+        keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+                .onRefreshBatteryInfo(getChargingBattery());
+        verify(mCallback).onChargingStatusChanged(eq(true), any());
+    }
+
+    @Test
+    public void testChargingStatusReportsChargingLimitedWhenOverheated() {
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+        mProvider.startUsing(mCallback);
+        verify(mCallback).onChargingStatusChanged(false, null);
+        verify(mKeyguardUpdateMonitor)
+                .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+        keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+                .onRefreshBatteryInfo(getBatteryDefender());
+        verify(mResources).getString(eq(R.string.keyguard_plugged_in_charging_limited), any());
+    }
+
+    @Test
+    public void testChargingStatusReportsChargedWhenCharged() {
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+        mProvider.startUsing(mCallback);
+        verify(mCallback).onChargingStatusChanged(false, null);
+        verify(mKeyguardUpdateMonitor)
+                .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+        keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+                .onRefreshBatteryInfo(getChargedBattery());
+        verify(mResources).getString(R.string.keyguard_charged);
+    }
+
+    @Test
+    public void testChargingStatusReportsPluggedInWhenDockedAndChargingTimeUnknown() throws
+            RemoteException {
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+        mProvider.startUsing(mCallback);
+        verify(mCallback).onChargingStatusChanged(false, null);
+        verify(mKeyguardUpdateMonitor)
+                .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+        when(mBatteryInfo.computeChargeTimeRemaining()).thenReturn(-1L);
+        keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+                .onRefreshBatteryInfo(getChargingBattery());
+        verify(mResources).getString(
+                eq(R.string.keyguard_plugged_in_dock), any());
+    }
+
+    @Test
+    public void testChargingStatusReportsTimeRemainingWhenDockedAndCharging() throws
+            RemoteException {
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+        mProvider.startUsing(mCallback);
+        verify(mCallback).onChargingStatusChanged(false, null);
+        verify(mKeyguardUpdateMonitor)
+                .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+        when(mBatteryInfo.computeChargeTimeRemaining()).thenReturn(1L);
+        keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+                .onRefreshBatteryInfo(getChargingBattery());
+        verify(mResources).getString(
+                eq(R.string.keyguard_indication_charging_time_dock), any(), any());
+    }
+
+    private BatteryStatus getUnpluggedBattery() {
+        return new BatteryStatus(BatteryManager.BATTERY_STATUS_NOT_CHARGING,
+                80, BatteryManager.BATTERY_PLUGGED_ANY, BatteryManager.BATTERY_HEALTH_GOOD,
+                0, true);
+    }
+
+    private BatteryStatus getChargingBattery() {
+        return new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
+                80, BatteryManager.BATTERY_PLUGGED_DOCK,
+                BatteryManager.BATTERY_HEALTH_GOOD, 0, true);
+    }
+
+    private BatteryStatus getChargedBattery() {
+        return new BatteryStatus(BatteryManager.BATTERY_STATUS_FULL,
+                100, BatteryManager.BATTERY_PLUGGED_DOCK,
+                BatteryManager.BATTERY_HEALTH_GOOD, 0, true);
+    }
+
+    private BatteryStatus getBatteryDefender() {
+        return new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
+                80, BatteryManager.BATTERY_PLUGGED_DOCK,
+                BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE, 0, true);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/DirectBootConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/DirectBootConditionTest.kt
new file mode 100644
index 0000000..173f243
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/DirectBootConditionTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.lowlightclock
+
+import android.content.Intent
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.condition.Condition
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class DirectBootConditionTest : SysuiTestCase() {
+    @Mock private lateinit var userManager: UserManager
+    @Mock private lateinit var callback: Condition.Callback
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun receiverRegisteredOnStart() = runTest {
+        val condition = buildCondition(this)
+        // No receivers are registered yet
+        assertThat(fakeBroadcastDispatcher.numReceiversRegistered).isEqualTo(0)
+        condition.addCallback(callback)
+        advanceUntilIdle()
+        // Receiver is registered after a callback is added
+        assertThat(fakeBroadcastDispatcher.numReceiversRegistered).isEqualTo(1)
+        condition.removeCallback(callback)
+    }
+
+    @Test
+    fun unregisterReceiverOnStop() = runTest {
+        val condition = buildCondition(this)
+
+        condition.addCallback(callback)
+        advanceUntilIdle()
+
+        assertThat(fakeBroadcastDispatcher.numReceiversRegistered).isEqualTo(1)
+
+        condition.removeCallback(callback)
+        advanceUntilIdle()
+
+        // Receiver is unregistered when nothing is listening to the condition
+        assertThat(fakeBroadcastDispatcher.numReceiversRegistered).isEqualTo(0)
+    }
+
+    @Test
+    fun callbackTriggeredWhenUserUnlocked() = runTest {
+        val condition = buildCondition(this)
+
+        setUserUnlocked(false)
+        condition.addCallback(callback)
+        advanceUntilIdle()
+
+        assertThat(condition.isConditionMet).isTrue()
+
+        setUserUnlocked(true)
+        advanceUntilIdle()
+
+        assertThat(condition.isConditionMet).isFalse()
+        condition.removeCallback(callback)
+    }
+
+    private fun buildCondition(scope: CoroutineScope): DirectBootCondition {
+        return DirectBootCondition(fakeBroadcastDispatcher, userManager, scope)
+    }
+
+    private fun setUserUnlocked(unlocked: Boolean) {
+        whenever(userManager.isUserUnlocked).thenReturn(unlocked)
+        fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+            context,
+            Intent(Intent.ACTION_USER_UNLOCKED),
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.java
new file mode 100644
index 0000000..7297e0f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.lowlightclock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.statusbar.commandline.Command;
+import com.android.systemui.statusbar.commandline.CommandRegistry;
+
+import kotlin.jvm.functions.Function0;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ForceLowLightConditionTest extends SysuiTestCase {
+    @Mock
+    private CommandRegistry mCommandRegistry;
+
+    @Mock
+    private Condition.Callback mCallback;
+
+    @Mock
+    private PrintWriter mPrintWriter;
+
+    @Mock
+    CoroutineScope mScope;
+
+    private ForceLowLightCondition mCondition;
+    private Command mCommand;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mCondition = new ForceLowLightCondition(mScope, mCommandRegistry);
+        mCondition.addCallback(mCallback);
+        ArgumentCaptor<Function0<Command>> commandCaptor =
+                ArgumentCaptor.forClass(Function0.class);
+        verify(mCommandRegistry).registerCommand(eq(ForceLowLightCondition.COMMAND_ROOT),
+                commandCaptor.capture());
+        mCommand = commandCaptor.getValue().invoke();
+    }
+
+    @Test
+    public void testEnableLowLight() {
+        mCommand.execute(mPrintWriter,
+                Arrays.asList(ForceLowLightCondition.COMMAND_ENABLE_LOW_LIGHT));
+        verify(mCallback).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionSet()).isTrue();
+        assertThat(mCondition.isConditionMet()).isTrue();
+    }
+
+    @Test
+    public void testDisableLowLight() {
+        mCommand.execute(mPrintWriter,
+                Arrays.asList(ForceLowLightCondition.COMMAND_DISABLE_LOW_LIGHT));
+        verify(mCallback).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionSet()).isTrue();
+        assertThat(mCondition.isConditionMet()).isFalse();
+    }
+
+    @Test
+    public void testClearEnableLowLight() {
+        mCommand.execute(mPrintWriter,
+                Arrays.asList(ForceLowLightCondition.COMMAND_ENABLE_LOW_LIGHT));
+        verify(mCallback).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionSet()).isTrue();
+        assertThat(mCondition.isConditionMet()).isTrue();
+        Mockito.clearInvocations(mCallback);
+        mCommand.execute(mPrintWriter,
+                Arrays.asList(ForceLowLightCondition.COMMAND_CLEAR_LOW_LIGHT));
+        verify(mCallback).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionSet()).isFalse();
+        assertThat(mCondition.isConditionMet()).isFalse();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockAnimationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockAnimationProviderTest.kt
new file mode 100644
index 0000000..663880f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockAnimationProviderTest.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.lowlightclock
+
+import android.animation.Animator
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class LowLightClockAnimationProviderTest : SysuiTestCase() {
+
+    private val underTest by lazy {
+        LowLightClockAnimationProvider(
+            Y_TRANSLATION_ANIMATION_OFFSET,
+            Y_TRANSLATION_ANIMATION_DURATION_MILLIS,
+            ALPHA_ANIMATION_IN_START_DELAY_MILLIS,
+            ALPHA_ANIMATION_DURATION_MILLIS,
+        )
+    }
+
+    @Test
+    fun animationOutEndsImmediatelyIfViewIsNull() {
+        val animator = underTest.provideAnimationOut(null, null)
+
+        val listener = mock<Animator.AnimatorListener>()
+        animator.addListener(listener)
+
+        animator.start()
+        verify(listener).onAnimationStart(any(), eq(false))
+        verify(listener).onAnimationEnd(any(), eq(false))
+    }
+
+    @Test
+    fun animationInEndsImmediatelyIfViewIsNull() {
+        val animator = underTest.provideAnimationIn(null, null)
+
+        val listener = mock<Animator.AnimatorListener>()
+        animator.addListener(listener)
+
+        animator.start()
+        verify(listener).onAnimationStart(any(), eq(false))
+        verify(listener).onAnimationEnd(any(), eq(false))
+    }
+
+    private companion object {
+        const val Y_TRANSLATION_ANIMATION_OFFSET = 100
+        const val Y_TRANSLATION_ANIMATION_DURATION_MILLIS = 100L
+        const val ALPHA_ANIMATION_IN_START_DELAY_MILLIS = 200L
+        const val ALPHA_ANIMATION_DURATION_MILLIS = 300L
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockDreamServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockDreamServiceTest.java
new file mode 100644
index 0000000..22a13cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockDreamServiceTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.lowlightclock;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.animation.Animator;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.dream.lowlight.LowLightTransitionCoordinator;
+import com.android.systemui.SysuiTestCase;
+
+import com.google.android.systemui.lowlightclock.LowLightClockDreamService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class LowLightClockDreamServiceTest extends SysuiTestCase {
+    @Mock
+    private ChargingStatusProvider mChargingStatusProvider;
+    @Mock
+    private LowLightDisplayController mDisplayController;
+    @Mock
+    private LowLightClockAnimationProvider mAnimationProvider;
+    @Mock
+    private LowLightTransitionCoordinator mLowLightTransitionCoordinator;
+    @Mock
+    Animator mAnimationInAnimator;
+    @Mock
+    Animator mAnimationOutAnimator;
+
+    private LowLightClockDreamService mService;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mService = new LowLightClockDreamService(
+                mChargingStatusProvider,
+                mAnimationProvider,
+                mLowLightTransitionCoordinator,
+                Optional.of(() -> mDisplayController));
+
+        when(mAnimationProvider.provideAnimationIn(any(), any())).thenReturn(mAnimationInAnimator);
+        when(mAnimationProvider.provideAnimationOut(any())).thenReturn(
+                mAnimationOutAnimator);
+    }
+
+    @Test
+    public void testSetDbmStateWhenSupported() throws RemoteException {
+        when(mDisplayController.isDisplayBrightnessModeSupported()).thenReturn(true);
+
+        mService.onDreamingStarted();
+
+        verify(mDisplayController).setDisplayBrightnessModeEnabled(true);
+    }
+
+    @Test
+    public void testNotSetDbmStateWhenNotSupported() throws RemoteException {
+        when(mDisplayController.isDisplayBrightnessModeSupported()).thenReturn(false);
+
+        mService.onDreamingStarted();
+
+        verify(mDisplayController, never()).setDisplayBrightnessModeEnabled(anyBoolean());
+    }
+
+    @Test
+    public void testClearDbmState() throws RemoteException {
+        when(mDisplayController.isDisplayBrightnessModeSupported()).thenReturn(true);
+
+        mService.onDreamingStarted();
+        clearInvocations(mDisplayController);
+
+        mService.onDreamingStopped();
+
+        verify(mDisplayController).setDisplayBrightnessModeEnabled(false);
+    }
+
+    @Test
+    public void testAnimationsStartedOnDreamingStarted() {
+        mService.onDreamingStarted();
+
+        // Entry animation started.
+        verify(mAnimationInAnimator).start();
+    }
+
+    @Test
+    public void testAnimationsStartedOnWakeUp() {
+        // Start dreaming then wake up.
+        mService.onDreamingStarted();
+        mService.onWakeUp();
+
+        // Entry animation started.
+        verify(mAnimationInAnimator).cancel();
+
+        // Exit animation started.
+        verify(mAnimationOutAnimator).start();
+    }
+
+    @Test
+    public void testAnimationsStartedBeforeExitingLowLight() {
+        mService.onBeforeExitLowLight();
+
+        // Exit animation started.
+        verify(mAnimationOutAnimator).start();
+    }
+
+    @Test
+    public void testWakeUpAnimationCancelledOnDetach() {
+        mService.onWakeUp();
+
+        // Exit animation started.
+        verify(mAnimationOutAnimator).start();
+
+        mService.onDetachedFromWindow();
+
+        verify(mAnimationOutAnimator).cancel();
+    }
+
+    @Test
+    public void testExitLowLightAnimationCancelledOnDetach() {
+        mService.onBeforeExitLowLight();
+
+        // Exit animation started.
+        verify(mAnimationOutAnimator).start();
+
+        mService.onDetachedFromWindow();
+
+        verify(mAnimationOutAnimator).cancel();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.java
new file mode 100644
index 0000000..2c21624
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.lowlightclock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.SysuiTestCase;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class LowLightConditionTest extends SysuiTestCase {
+    @Mock
+    private AmbientLightModeMonitor mAmbientLightModeMonitor;
+    @Mock
+    private UiEventLogger mUiEventLogger;
+    @Mock
+    CoroutineScope mScope;
+    private LowLightCondition mCondition;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mCondition = new LowLightCondition(mScope, mAmbientLightModeMonitor, mUiEventLogger);
+        mCondition.start();
+    }
+
+    @Test
+    public void testLowLightFalse() {
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
+        assertThat(mCondition.isConditionMet()).isFalse();
+    }
+
+    @Test
+    public void testLowLightTrue() {
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+        assertThat(mCondition.isConditionMet()).isTrue();
+    }
+
+    @Test
+    public void testUndecidedLowLightStateIgnored() {
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+        assertThat(mCondition.isConditionMet()).isTrue();
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED);
+        assertThat(mCondition.isConditionMet()).isTrue();
+    }
+
+    @Test
+    public void testLowLightChange() {
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
+        assertThat(mCondition.isConditionMet()).isFalse();
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+        assertThat(mCondition.isConditionMet()).isTrue();
+    }
+
+    @Test
+    public void testResetIsConditionMetUponStop() {
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+        assertThat(mCondition.isConditionMet()).isTrue();
+
+        mCondition.stop();
+        assertThat(mCondition.isConditionMet()).isFalse();
+    }
+
+    @Test
+    public void testLoggingAmbientLightNotLowToLow() {
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+        // Only logged once.
+        verify(mUiEventLogger, times(1)).log(any());
+        // Logged with the correct state.
+        verify(mUiEventLogger).log(LowLightDockEvent.AMBIENT_LIGHT_TO_DARK);
+    }
+
+    @Test
+    public void testLoggingAmbientLightLowToLow() {
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+        reset(mUiEventLogger);
+
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+        // Doesn't log.
+        verify(mUiEventLogger, never()).log(any());
+    }
+
+    @Test
+    public void testLoggingAmbientLightNotLowToNotLow() {
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
+        // Doesn't log.
+        verify(mUiEventLogger, never()).log(any());
+    }
+
+    @Test
+    public void testLoggingAmbientLightLowToNotLow() {
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+        reset(mUiEventLogger);
+
+        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
+        // Only logged once.
+        verify(mUiEventLogger).log(any());
+        // Logged with the correct state.
+        verify(mUiEventLogger).log(LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT);
+    }
+
+    private void changeLowLightMode(int mode) {
+        ArgumentCaptor<AmbientLightModeMonitor.Callback> ambientLightCallbackCaptor =
+                ArgumentCaptor.forClass(AmbientLightModeMonitor.Callback.class);
+        verify(mAmbientLightModeMonitor).start(ambientLightCallbackCaptor.capture());
+        ambientLightCallbackCaptor.getValue().onChange(mode);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.java
new file mode 100644
index 0000000..69485e8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.lowlightclock;
+
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT;
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR;
+import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.dream.lowlight.LowLightDreamManager;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
+
+import dagger.Lazy;
+
+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.MockitoAnnotations;
+
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class LowLightMonitorTest extends SysuiTestCase {
+
+    @Mock
+    private Lazy<LowLightDreamManager> mLowLightDreamManagerLazy;
+    @Mock
+    private LowLightDreamManager mLowLightDreamManager;
+    @Mock
+    private Monitor mMonitor;
+    @Mock
+    private ScreenLifecycle mScreenLifecycle;
+    @Mock
+    private LowLightLogger mLogger;
+
+    private LowLightMonitor mLowLightMonitor;
+
+    @Mock
+    Lazy<Set<Condition>> mLazyConditions;
+
+    @Mock
+    private PackageManager mPackageManager;
+
+    @Mock
+    private ComponentName mDreamComponent;
+
+    Condition mCondition = mock(Condition.class);
+    Set<Condition> mConditionSet = Set.of(mCondition);
+
+    @Captor
+    ArgumentCaptor<Monitor.Subscription> mPreconditionsSubscriptionCaptor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mLowLightDreamManagerLazy.get()).thenReturn(mLowLightDreamManager);
+        when(mLazyConditions.get()).thenReturn(mConditionSet);
+        mLowLightMonitor = new LowLightMonitor(mLowLightDreamManagerLazy,
+            mMonitor, mLazyConditions, mScreenLifecycle, mLogger, mDreamComponent,
+                mPackageManager);
+    }
+
+    @Test
+    public void testSetAmbientLowLightWhenInLowLight() {
+        mLowLightMonitor.onConditionsChanged(true);
+        // Verify setting low light when condition is true
+        verify(mLowLightDreamManager).setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
+    }
+
+    @Test
+    public void testExitAmbientLowLightWhenNotInLowLight() {
+        mLowLightMonitor.onConditionsChanged(true);
+        mLowLightMonitor.onConditionsChanged(false);
+        // Verify ambient light toggles back to light mode regular
+        verify(mLowLightDreamManager).setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);
+    }
+
+    @Test
+    public void testStartMonitorLowLightConditionsWhenScreenTurnsOn() {
+        mLowLightMonitor.onScreenTurnedOn();
+
+        // Verify subscribing to low light conditions monitor when screen turns on.
+        verify(mMonitor).addSubscription(any());
+    }
+
+    @Test
+    public void testStopMonitorLowLightConditionsWhenScreenTurnsOff() {
+        final Monitor.Subscription.Token token = mock(Monitor.Subscription.Token.class);
+        when(mMonitor.addSubscription(any())).thenReturn(token);
+        mLowLightMonitor.onScreenTurnedOn();
+
+        // Verify removing subscription when screen turns off.
+        mLowLightMonitor.onScreenTurnedOff();
+        verify(mMonitor).removeSubscription(token);
+    }
+
+    @Test
+    public void testSubscribeToLowLightConditionsOnlyOnceWhenScreenTurnsOn() {
+        final Monitor.Subscription.Token token = mock(Monitor.Subscription.Token.class);
+        when(mMonitor.addSubscription(any())).thenReturn(token);
+
+        mLowLightMonitor.onScreenTurnedOn();
+        mLowLightMonitor.onScreenTurnedOn();
+        // Verify subscription is only added once.
+        verify(mMonitor, times(1)).addSubscription(any());
+    }
+
+    @Test
+    public void testSubscribedToExpectedConditions() {
+        final Monitor.Subscription.Token token = mock(Monitor.Subscription.Token.class);
+        when(mMonitor.addSubscription(any())).thenReturn(token);
+
+        mLowLightMonitor.onScreenTurnedOn();
+        mLowLightMonitor.onScreenTurnedOn();
+        Set<Condition> conditions = captureConditions();
+        // Verify Monitor is subscribed to the expected conditions
+        assertThat(conditions).isEqualTo(mConditionSet);
+    }
+
+    @Test
+    public void testNotUnsubscribeIfNotSubscribedWhenScreenTurnsOff() {
+        mLowLightMonitor.onScreenTurnedOff();
+
+        // Verify doesn't remove subscription since there is none.
+        verify(mMonitor, never()).removeSubscription(any());
+    }
+
+    @Test
+    public void testSubscribeIfScreenIsOnWhenStarting() {
+        when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_ON);
+        mLowLightMonitor.start();
+        // Verify to add subscription on start if the screen state is on
+        verify(mMonitor, times(1)).addSubscription(any());
+    }
+
+    @Test
+    public void testNoSubscribeIfDreamNotPresent() {
+        LowLightMonitor lowLightMonitor = new LowLightMonitor(mLowLightDreamManagerLazy,
+                mMonitor, mLazyConditions, mScreenLifecycle, mLogger, null, mPackageManager);
+        when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_ON);
+        lowLightMonitor.start();
+        verify(mScreenLifecycle, never()).addObserver(any());
+    }
+
+    private Set<Condition> captureConditions() {
+        verify(mMonitor).addSubscription(mPreconditionsSubscriptionCaptor.capture());
+        return mPreconditionsSubscriptionCaptor.getValue().getConditions();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.java
new file mode 100644
index 0000000..366c071
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.lowlightclock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.settings.SecureSettings;
+
+import kotlinx.coroutines.CoroutineScope;
+
+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.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ScreenSaverEnabledConditionTest extends SysuiTestCase {
+    @Mock
+    private Resources mResources;
+    @Mock
+    private SecureSettings mSecureSettings;
+    @Mock
+    CoroutineScope mScope;
+    @Captor
+    private ArgumentCaptor<ContentObserver> mSettingsObserverCaptor;
+    private ScreenSaverEnabledCondition mCondition;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        // Default dreams to enabled by default
+        doReturn(true).when(mResources).getBoolean(
+                com.android.internal.R.bool.config_dreamsEnabledByDefault);
+
+        mCondition = new ScreenSaverEnabledCondition(mScope, mResources, mSecureSettings);
+    }
+
+    @Test
+    public void testScreenSaverInitiallyEnabled() {
+        setScreenSaverEnabled(true);
+        mCondition.start();
+        assertThat(mCondition.isConditionMet()).isTrue();
+    }
+
+    @Test
+    public void testScreenSaverInitiallyDisabled() {
+        setScreenSaverEnabled(false);
+        mCondition.start();
+        assertThat(mCondition.isConditionMet()).isFalse();
+    }
+
+    @Test
+    public void testScreenSaverStateChanges() {
+        setScreenSaverEnabled(false);
+        mCondition.start();
+        assertThat(mCondition.isConditionMet()).isFalse();
+
+        setScreenSaverEnabled(true);
+        final ContentObserver observer = captureSettingsObserver();
+        observer.onChange(/* selfChange= */ false);
+        assertThat(mCondition.isConditionMet()).isTrue();
+    }
+
+    private void setScreenSaverEnabled(boolean enabled) {
+        when(mSecureSettings.getIntForUser(eq(Settings.Secure.SCREENSAVER_ENABLED), anyInt(),
+                eq(UserHandle.USER_CURRENT))).thenReturn(enabled ? 1 : 0);
+    }
+
+    private ContentObserver captureSettingsObserver() {
+        verify(mSecureSettings).registerContentObserverForUserSync(
+                eq(Settings.Secure.SCREENSAVER_ENABLED),
+                mSettingsObserverCaptor.capture(), eq(UserHandle.USER_CURRENT));
+        return mSettingsObserverCaptor.getValue();
+    }
+}
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 e2a2b7a..38dc03e 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
@@ -41,7 +41,6 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
@@ -90,7 +89,6 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
         MediaPlayerData.clear()
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
         mediaDataFilter =
             LegacyMediaDataFilterImpl(
                 context,
@@ -100,7 +98,7 @@
                 executor,
                 clock,
                 logger,
-                mediaFlags
+                mediaFlags,
             )
         mediaDataFilter.mediaDataManager = mediaDataManager
         mediaDataFilter.addListener(listener)
@@ -114,7 +112,7 @@
                 userId = USER_MAIN,
                 packageName = PACKAGE,
                 instanceId = INSTANCE_ID,
-                appUid = APP_UID
+                appUid = APP_UID,
             )
         dataGuest = dataMain.copy(userId = USER_GUEST)
         dataPrivateProfile = dataMain.copy(userId = PRIVATE_PROFILE)
@@ -476,7 +474,7 @@
                 eq(dataCurrentAndActive),
                 eq(true),
                 eq(100),
-                eq(true)
+                eq(true),
             )
         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
         // Smartspace update shouldn't be propagated for the empty rec list.
@@ -505,7 +503,7 @@
                 eq(dataCurrentAndActive),
                 eq(true),
                 eq(100),
-                eq(true)
+                eq(true),
             )
         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
         // Smartspace update should also be propagated but not prioritized.
@@ -542,7 +540,7 @@
                 eq(dataCurrentAndActive),
                 eq(true),
                 eq(100),
-                eq(true)
+                eq(true),
             )
 
         mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
@@ -553,61 +551,6 @@
     }
 
     @Test
-    fun testOnSmartspaceLoaded_persistentEnabled_isInactive_notifiesListeners() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-        whenever(smartspaceData.isActive).thenReturn(false)
-
-        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
-
-        verify(listener)
-            .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
-        assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
-        assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
-    }
-
-    @Test
-    fun testOnSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactive() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-        whenever(smartspaceData.isActive).thenReturn(false)
-
-        // If there is media that was recently played but inactive
-        val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
-        mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
-        verify(listener)
-            .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
-
-        // And an inactive recommendation is loaded
-        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
-
-        // Smartspace is loaded but the media stays inactive
-        verify(listener)
-            .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
-        verify(listener, never())
-            .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
-        assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
-        assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
-    }
-
-    @Test
-    fun testOnSwipeToDismiss_persistentEnabled_recommendationSetInactive() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-
-        val data =
-            EMPTY_SMARTSPACE_MEDIA_DATA.copy(
-                targetId = SMARTSPACE_KEY,
-                isActive = true,
-                packageName = SMARTSPACE_PACKAGE,
-                recommendations = listOf(smartspaceMediaRecommendationItem),
-            )
-        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data)
-        mediaDataFilter.onSwipeToDismiss()
-
-        verify(mediaDataManager).setRecommendationInactive(eq(SMARTSPACE_KEY))
-        verify(mediaDataManager, never())
-            .dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong())
-    }
-
-    @Test
     fun testSmartspaceLoaded_shouldTriggerResume_doesTrigger() {
         // WHEN we have media that was recently played, but not currently active
         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
@@ -629,7 +572,7 @@
                 eq(dataCurrentAndActive),
                 eq(true),
                 eq(100),
-                eq(true)
+                eq(true),
             )
         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
         // And send the smartspace data, but not prioritized
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 2815b9769..676d8fa0 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
@@ -54,7 +54,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.Flags.MEDIA_REMOTE_RESUME
 import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
-import com.android.systemui.flags.Flags.MEDIA_RETAIN_RECOMMENDATIONS
 import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testDispatcher
@@ -236,6 +235,7 @@
                 modifyNotification(context).also {
                     it.setSmallIcon(android.R.drawable.ic_media_pause)
                     it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+                    it.setContentIntent(getNewPendingIntent())
                 }
                 build()
             }
@@ -293,7 +293,6 @@
         fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false)
         fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, false)
         fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, false)
-        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, false)
         whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
         whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(false)
     }
@@ -2158,6 +2157,28 @@
         verify(kosmos.mediaLogger, never()).logDuplicateMediaNotification(eq(KEY))
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_POSTS_OPTIMIZATION)
+    fun postDifferentIntentNotifications_CallsListeners() {
+        addNotificationAndLoad()
+        reset(listener)
+        mediaNotification =
+            mediaNotification.also { it.notification.contentIntent = getNewPendingIntent() }
+        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+
+        testScope.assertRunAllReady(foreground = 1, background = 1)
+        verify(listener)
+            .onMediaDataLoaded(
+                eq(KEY),
+                eq(KEY),
+                capture(mediaDataCaptor),
+                eq(true),
+                eq(0),
+                eq(false),
+            )
+        verify(kosmos.mediaLogger, never()).logDuplicateMediaNotification(eq(KEY))
+    }
+
     private fun TestScope.assertRunAllReady(foreground: Int = 0, background: Int = 0) {
         runCurrent()
         if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
@@ -2237,4 +2258,14 @@
         backgroundExecutor.runAllReady()
         foregroundExecutor.runAllReady()
     }
+
+    private fun getNewPendingIntent(): PendingIntent {
+        val intent = Intent().setAction(null)
+        return PendingIntent.getBroadcast(
+            mContext,
+            1,
+            intent,
+            PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE,
+        )
+    }
 }
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 3705909..811d2e2 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
@@ -20,7 +20,6 @@
 import android.app.smartspace.SmartspaceAction
 import android.graphics.drawable.Icon
 import android.os.Bundle
-import android.os.Process
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -42,7 +41,6 @@
 import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaSmartspaceLogger
 import com.android.systemui.media.controls.util.MediaUiEventLogger
-import com.android.systemui.media.controls.util.SmallHash
 import com.android.systemui.media.controls.util.mediaSmartspaceLogger
 import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
 import com.android.systemui.settings.UserTracker
@@ -60,7 +58,6 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
 import org.mockito.Mockito.never
@@ -121,7 +118,6 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
         MediaPlayerData.clear()
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
         testScope = TestScope()
         mediaDataFilter =
             MediaDataFilterImpl(
@@ -148,7 +144,7 @@
                 userId = USER_MAIN,
                 packageName = PACKAGE,
                 instanceId = INSTANCE_ID,
-                appUid = APP_UID
+                appUid = APP_UID,
             )
         dataGuest = dataMain.copy(userId = USER_GUEST, instanceId = INSTANCE_ID_GUEST)
         dataPrivateProfile = dataMain.copy(userId = PRIVATE_PROFILE, instanceId = INSTANCE_ID_GUEST)
@@ -302,7 +298,7 @@
                     eq(dataMain),
                     anyBoolean(),
                     anyInt(),
-                    anyBoolean()
+                    anyBoolean(),
                 )
             verify(mediaLogger, never())
                 .logMediaLoaded(eq(dataMain.instanceId), anyBoolean(), anyString())
@@ -406,7 +402,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isFalse()
@@ -425,7 +421,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isTrue()
@@ -444,7 +440,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isFalse()
@@ -463,7 +459,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isFalse()
@@ -483,7 +479,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isTrue()
@@ -513,7 +509,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isFalse()
@@ -524,7 +520,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isFalse()
@@ -561,7 +557,7 @@
                 anyInt(),
                 anyInt(),
                 anyInt(),
-                eq(true)
+                eq(true),
             )
         verify(mediaDataProcessor).setInactive(eq(KEY), eq(true), eq(true))
     }
@@ -584,7 +580,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isTrue()
@@ -613,7 +609,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isFalse()
@@ -641,7 +637,7 @@
             val controlCommonModel =
                 MediaCommonModel.MediaControl(
                     MediaDataLoadingModel.Loaded(dataMain.instanceId),
-                    true
+                    true,
                 )
             val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
@@ -653,7 +649,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isTrue()
@@ -689,7 +685,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isFalse()
@@ -715,7 +711,7 @@
             val controlCommonModel =
                 MediaCommonModel.MediaControl(
                     MediaDataLoadingModel.Loaded(dataMain.instanceId),
-                    true
+                    true,
                 )
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
             repository.setOrderedMedia()
@@ -737,7 +733,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isFalse()
@@ -784,7 +780,7 @@
                 controlCommonModel.copy(
                     mediaLoadingModel.copy(
                         receivedSmartspaceCardLatency = 100,
-                        isSsReactivated = true
+                        isSsReactivated = true,
                     )
                 )
             assertThat(currentMedia).containsExactly(controlCommonModel)
@@ -792,7 +788,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isTrue()
@@ -803,13 +799,13 @@
                     eq(dataCurrentAndActive),
                     eq(true),
                     eq(100),
-                    eq(true)
+                    eq(true),
                 )
             verify(mediaLogger)
                 .logMediaLoaded(
                     eq(dataCurrentAndActive.instanceId),
                     eq(dataCurrentAndActive.active),
-                    anyString()
+                    anyString(),
                 )
             // Smartspace update shouldn't be propagated for the empty rec list.
             verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
@@ -857,19 +853,19 @@
                     eq(dataCurrentAndActive),
                     eq(true),
                     eq(100),
-                    eq(true)
+                    eq(true),
                 )
             verify(mediaLogger)
                 .logMediaLoaded(
                     eq(dataCurrentAndActive.instanceId),
                     eq(dataCurrentAndActive.active),
-                    anyString()
+                    anyString(),
                 )
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isTrue()
@@ -878,7 +874,7 @@
                 controlCommonModel.copy(
                     mediaLoadingModel.copy(
                         receivedSmartspaceCardLatency = 100,
-                        isSsReactivated = true
+                        isSsReactivated = true,
                     )
                 )
             assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
@@ -907,7 +903,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isFalse()
@@ -924,7 +920,7 @@
             val controlCommonModel =
                 MediaCommonModel.MediaControl(
                     MediaDataLoadingModel.Loaded(dataMain.instanceId),
-                    true
+                    true,
                 )
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
@@ -948,13 +944,13 @@
                     eq(dataCurrentAndActive),
                     eq(true),
                     eq(100),
-                    eq(true)
+                    eq(true),
                 )
             verify(mediaLogger)
                 .logMediaLoaded(
                     eq(dataCurrentAndActive.instanceId),
                     eq(dataCurrentAndActive.active),
-                    anyString()
+                    anyString(),
                 )
 
             mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
@@ -966,7 +962,7 @@
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isFalse()
@@ -974,129 +970,6 @@
         }
 
     @Test
-    fun onSmartspaceLoaded_persistentEnabled_isInactive() =
-        testScope.runTest {
-            val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
-            val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
-            val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val currentMedia by collectLastValue(repository.currentMedia)
-            val recsCommonModel =
-                MediaCommonModel.MediaRecommendations(
-                    SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
-                )
-            whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-            whenever(smartspaceData.isActive).thenReturn(false)
-
-            mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
-
-            verify(listener)
-                .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
-            verify(mediaLogger).logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(false), anyString())
-            assertThat(currentMedia).containsExactly(recsCommonModel)
-            assertThat(
-                    hasActiveMediaOrRecommendation(
-                        selectedUserEntries,
-                        smartspaceMediaData,
-                        reactivatedKey
-                    )
-                )
-                .isFalse()
-            assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData))
-                .isTrue()
-        }
-
-    @Test
-    fun onSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactive() =
-        testScope.runTest {
-            val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
-            val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
-            val reactivatedKey by collectLastValue(repository.reactivatedId)
-            val currentMedia by collectLastValue(repository.currentMedia)
-            val recsCommonModel =
-                MediaCommonModel.MediaRecommendations(
-                    SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
-                )
-            val controlCommonModel =
-                MediaCommonModel.MediaControl(
-                    MediaDataLoadingModel.Loaded(dataMain.instanceId),
-                    true
-                )
-
-            whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-            whenever(smartspaceData.isActive).thenReturn(false)
-
-            // If there is media that was recently played but inactive
-            val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
-            mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
-            repository.setOrderedMedia()
-
-            verify(listener)
-                .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
-            verify(mediaLogger)
-                .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString())
-            assertThat(currentMedia).containsExactly(controlCommonModel)
-
-            reset(mediaLogger)
-
-            // And an inactive recommendation is loaded
-            mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
-
-            // Smartspace is loaded but the media stays inactive
-            verify(listener)
-                .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
-            verify(mediaLogger).logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(false), anyString())
-            verify(listener, never())
-                .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
-            verify(mediaLogger, never()).logMediaLoaded(any(), anyBoolean(), anyString())
-            assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
-            assertThat(
-                    hasActiveMediaOrRecommendation(
-                        selectedUserEntries,
-                        smartspaceMediaData,
-                        reactivatedKey
-                    )
-                )
-                .isFalse()
-            assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData))
-                .isTrue()
-        }
-
-    @Test
-    fun onSwipeToDismiss_persistentEnabled_recommendationSetInactive() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-
-        val data =
-            EMPTY_SMARTSPACE_MEDIA_DATA.copy(
-                targetId = SMARTSPACE_KEY,
-                isActive = true,
-                packageName = SMARTSPACE_PACKAGE,
-                recommendations =
-                    listOf(
-                        smartspaceMediaRecommendationItem,
-                        smartspaceMediaRecommendationItem,
-                        smartspaceMediaRecommendationItem
-                    ),
-            )
-        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data)
-
-        mediaDataFilter.onSwipeToDismiss(1)
-
-        verify(smartspaceLogger)
-            .logSmartspaceCardUIEvent(
-                MediaSmartspaceLogger.SMARTSPACE_CARD_DISMISS_EVENT,
-                SmallHash.hash(data.targetId),
-                Process.INVALID_UID,
-                surface = 1,
-                cardinality = 1,
-                isRecommendationCard = true,
-                isSwipeToDismiss = true
-            )
-        verify(mediaDataProcessor).setRecommendationInactive(eq(SMARTSPACE_KEY))
-        verify(mediaDataProcessor, never())
-            .dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong())
-    }
-
-    @Test
     fun smartspaceLoaded_shouldTriggerResume_doesTrigger() =
         testScope.runTest {
             val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
@@ -1133,7 +1006,7 @@
                 controlCommonModel.copy(
                     mediaLoadingModel.copy(
                         receivedSmartspaceCardLatency = 100,
-                        isSsReactivated = true
+                        isSsReactivated = true,
                     )
                 )
             verify(listener)
@@ -1143,20 +1016,20 @@
                     eq(dataCurrentAndActive),
                     eq(true),
                     eq(100),
-                    eq(true)
+                    eq(true),
                 )
             verify(mediaLogger)
                 .logMediaLoaded(
                     eq(dataCurrentAndActive.instanceId),
                     eq(dataCurrentAndActive.active),
-                    anyString()
+                    anyString(),
                 )
             assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
                         smartspaceMediaData,
-                        reactivatedKey
+                        reactivatedKey,
                     )
                 )
                 .isTrue()
@@ -1177,7 +1050,7 @@
             val controlCommonModel =
                 MediaCommonModel.MediaControl(
                     MediaDataLoadingModel.Loaded(dataMain.instanceId),
-                    true
+                    true,
                 )
 
             // WHEN we have media that was recently played, but not currently active
@@ -1213,7 +1086,7 @@
     private fun hasActiveMediaOrRecommendation(
         entries: Map<InstanceId, MediaData>?,
         smartspaceMediaData: SmartspaceMediaData?,
-        reactivatedId: InstanceId?
+        reactivatedId: InstanceId?,
     ): Boolean {
         if (entries == null || smartspaceMediaData == null) {
             return false
@@ -1229,17 +1102,13 @@
 
     private fun hasAnyMediaOrRecommendation(
         entries: Map<InstanceId, MediaData>?,
-        smartspaceMediaData: SmartspaceMediaData?
+        smartspaceMediaData: SmartspaceMediaData?,
     ): Boolean {
         if (entries == null || smartspaceMediaData == null) {
             return false
         }
         return entries.isNotEmpty() ||
-            (if (mediaFlags.isPersistentSsCardEnabled()) {
-                smartspaceMediaData.isValid()
-            } else {
-                smartspaceMediaData.isActive && smartspaceMediaData.isValid()
-            })
+            (smartspaceMediaData.isActive && smartspaceMediaData.isValid())
     }
 
     private fun hasAnyMedia(entries: Map<InstanceId, MediaData>?): Boolean {
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 b9ebce8..496b319 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
@@ -57,7 +57,6 @@
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags.MEDIA_REMOTE_RESUME
 import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
-import com.android.systemui.flags.Flags.MEDIA_RETAIN_RECOMMENDATIONS
 import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testDispatcher
@@ -252,6 +251,7 @@
         verify(mediaTimeoutListener).stateCallback = capture(stateCallbackCaptor)
         verify(mediaTimeoutListener).sessionCallback = capture(sessionCallbackCaptor)
         session = MediaSession(context, "MediaDataProcessorTestSession")
+
         mediaNotification =
             SbnBuilder().run {
                 setUser(UserHandle(USER_ID))
@@ -259,6 +259,7 @@
                 modifyNotification(context).also {
                     it.setSmallIcon(android.R.drawable.ic_media_pause)
                     it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+                    it.setContentIntent(getNewPendingIntent())
                 }
                 build()
             }
@@ -316,7 +317,6 @@
         fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false)
         fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, false)
         fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, false)
-        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, false)
         whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
         whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(false)
     }
@@ -1524,7 +1524,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
     fun postWithPlaybackActions_drawablesReused() {
         whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
         whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
@@ -1557,7 +1557,7 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
     fun postWithPlaybackActions_drawablesNotReused() {
         whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
         whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
@@ -2252,6 +2252,33 @@
         verify(kosmos.mediaLogger, never()).logDuplicateMediaNotification(eq(KEY))
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_POSTS_OPTIMIZATION)
+    fun postDifferentIntentNotifications_CallsListeners() {
+        whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+        whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+
+        mediaDataProcessor.addInternalListener(mediaDataFilter)
+        mediaDataFilter.mediaDataProcessor = mediaDataProcessor
+        addNotificationAndLoad()
+        reset(listener)
+        mediaNotification =
+            mediaNotification.also { it.notification.contentIntent = getNewPendingIntent() }
+        mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
+
+        testScope.assertRunAllReady(foreground = 1, background = 1)
+        verify(listener)
+            .onMediaDataLoaded(
+                eq(KEY),
+                eq(KEY),
+                capture(mediaDataCaptor),
+                eq(true),
+                eq(0),
+                eq(false),
+            )
+        verify(kosmos.mediaLogger, never()).logDuplicateMediaNotification(eq(KEY))
+    }
+
     private fun TestScope.assertRunAllReady(foreground: Int = 0, background: Int = 0) {
         runCurrent()
         if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
@@ -2331,4 +2358,14 @@
         runCurrent()
         advanceUntilIdle()
     }
+
+    private fun getNewPendingIntent(): PendingIntent {
+        val intent = Intent().setAction(null)
+        return PendingIntent.getBroadcast(
+            mContext,
+            1,
+            intent,
+            PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE,
+        )
+    }
 }
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 0c8d880..175e8d4 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
@@ -169,7 +169,7 @@
 
         context.orCreateTestableResources.addOverride(
             R.drawable.ic_media_home_devices,
-            OTHER_DEVICE_ICON_STUB
+            OTHER_DEVICE_ICON_STUB,
         )
     }
 
@@ -384,7 +384,7 @@
         deviceCallback.onAboutToConnectDeviceAdded(
             "fakeAddress",
             "AboutToConnectDeviceName",
-            mock(Drawable::class.java)
+            mock(Drawable::class.java),
         )
         // Run and reset the executors and listeners so we only focus on new events.
         fakeBgExecutor.runAllReady()
@@ -423,7 +423,7 @@
     }
 
     @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    @EnableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
     fun onMediaDataLoaded_withRemotePlaybackType_usesNonNullRoutingSessionName_drawableReused() {
         whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
         whenever(routingSession.selectedRoutes).thenReturn(listOf("selectedRoute", "selectedRoute"))
@@ -437,7 +437,7 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    @DisableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
     fun onMediaDataLoaded_withRemotePlaybackType_usesNonNullRoutingSessionName_drawableNotReused() {
         whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
         whenever(routingSession.selectedRoutes).thenReturn(listOf("selectedRoute", "selectedRoute"))
@@ -479,7 +479,7 @@
 
     @Test
     @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
-    @EnableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    @EnableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
     fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_drawableReused() {
         whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
@@ -494,7 +494,7 @@
 
     @Test
     @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
-    @DisableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    @DisableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX)
     fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_drawableNotReused() {
         whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
@@ -856,7 +856,7 @@
     @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
     @EnableFlags(
         Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
-        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE
+        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX,
     )
     fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting_drawablesReused() {
         val broadcastCallback = setupBroadcastCallback()
@@ -874,7 +874,7 @@
     @Test
     @DisableFlags(
         Flags.FLAG_LEGACY_LE_AUDIO_SHARING,
-        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE
+        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX,
     )
     @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
     fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting_drawablesNotReused() {
@@ -893,7 +893,7 @@
     @Test
     @EnableFlags(
         Flags.FLAG_LEGACY_LE_AUDIO_SHARING,
-        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE
+        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX,
     )
     @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
     fun onBroadcastStarted_legacy_currentMediaDeviceDataIsNotBroadcasting_drawableReused() {
@@ -913,7 +913,7 @@
     @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
     @DisableFlags(
         Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
-        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE
+        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE_BUGFIX,
     )
     fun onBroadcastStarted_legacy_currentMediaDeviceDataIsNotBroadcasting_drawableNotReused() {
         val broadcastCallback = setupBroadcastCallback()
@@ -984,7 +984,7 @@
 
                 override fun onBroadcastMetadataChanged(
                     broadcastId: Int,
-                    metadata: BluetoothLeBroadcastMetadata
+                    metadata: BluetoothLeBroadcastMetadata,
                 ) {}
             }
 
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 33b61a09..6c8a46f 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
@@ -39,7 +39,6 @@
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
@@ -79,7 +78,6 @@
 import java.util.Locale
 import javax.inject.Provider
 import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -215,7 +213,6 @@
         verify(mediaHostStatesManager).addCallback(capture(hostStateCallback))
         whenever(mediaControlPanelFactory.get()).thenReturn(panel)
         whenever(panel.mediaViewController).thenReturn(mediaViewController)
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
         MediaPlayerData.clear()
         FakeExecutor.exhaustExecutors(bgExecutor)
         FakeExecutor.exhaustExecutors(uiExecutor)
@@ -834,47 +831,6 @@
         verify(pageIndicator, times(4)).setNumPages(any())
     }
 
-    @DisableSceneContainer
-    @Test
-    fun testRecommendation_persistentEnabled_newSmartspaceLoaded_updatesSort() {
-        verify(mediaDataManager).addListener(capture(listener))
-
-        testRecommendation_persistentEnabled_inactiveSmartspaceDataLoaded_isAdded()
-
-        // When an update to existing smartspace data is loaded
-        listener.value.onSmartspaceMediaDataLoaded(
-            SMARTSPACE_KEY,
-            EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
-            true,
-        )
-
-        // Then the carousel is updated
-        assertTrue(MediaPlayerData.playerKeys().elementAt(0).data.active)
-        assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(0).data.active)
-    }
-
-    @DisableSceneContainer
-    @Test
-    fun testRecommendation_persistentEnabled_inactiveSmartspaceDataLoaded_isAdded() {
-        verify(mediaDataManager).addListener(capture(listener))
-
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
-
-        // When inactive smartspace data is loaded
-        listener.value.onSmartspaceMediaDataLoaded(
-            SMARTSPACE_KEY,
-            EMPTY_SMARTSPACE_MEDIA_DATA,
-            false,
-        )
-
-        // Then it is added to the carousel with correct state
-        assertTrue(MediaPlayerData.playerKeys().elementAt(0).isSsMediaRec)
-        assertFalse(MediaPlayerData.playerKeys().elementAt(0).data.active)
-
-        assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(0).isSsMediaRec)
-        assertFalse(MediaPlayerData.visiblePlayerKeys().elementAt(0).data.active)
-    }
-
     @Test
     fun testOnLockDownMode_hideMediaCarousel() {
         whenever(keyguardUpdateMonitor.isUserInLockdown(context.userId)).thenReturn(true)
@@ -900,7 +856,6 @@
     @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
@@ -923,7 +878,6 @@
     @Test
     fun testKeyguardGone_showMediaCarousel_scene_container() =
         kosmos.testScope.runTest {
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             var updatedVisibility = false
             mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
             mediaCarouselController.mediaCarousel = mediaCarousel
@@ -940,7 +894,6 @@
     @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
@@ -969,7 +922,6 @@
     @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
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
index 86063ac..2715cb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
@@ -1512,6 +1512,60 @@
         assertThat(getNumberOfConnectDeviceButtons()).isEqualTo(1);
     }
 
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void selectedDevicesAddedInSameOrder() {
+        when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true);
+        doReturn(mMediaDevices)
+                .when(mLocalMediaManager)
+                .getSelectedMediaDevice();
+        mMediaSwitchingController.start(mCb);
+        reset(mCb);
+        mMediaSwitchingController.getMediaItemList().clear();
+
+        mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
+
+        List<MediaItem> items = mMediaSwitchingController.getMediaItemList();
+        assertThat(items.get(0).getMediaDevice().get()).isEqualTo(mMediaDevice1);
+        assertThat(items.get(1).getMediaDevice().get()).isEqualTo(mMediaDevice2);
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void selectedDevicesAddedInReverseOrder() {
+        when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true);
+        doReturn(mMediaDevices)
+                .when(mLocalMediaManager)
+                .getSelectedMediaDevice();
+        mMediaSwitchingController.start(mCb);
+        reset(mCb);
+        mMediaSwitchingController.getMediaItemList().clear();
+
+        mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
+
+        List<MediaItem> items = mMediaSwitchingController.getMediaItemList();
+        assertThat(items.get(0).getMediaDevice().get()).isEqualTo(mMediaDevice2);
+        assertThat(items.get(1).getMediaDevice().get()).isEqualTo(mMediaDevice1);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_SESSION_GROUPING)
+    @Test
+    public void firstSelectedDeviceIsFirstDeviceInGroupIsTrue() {
+        when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true);
+        doReturn(mMediaDevices)
+                .when(mLocalMediaManager)
+                .getSelectedMediaDevice();
+        mMediaSwitchingController.start(mCb);
+        reset(mCb);
+        mMediaSwitchingController.getMediaItemList().clear();
+
+        mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
+
+        List<MediaItem> items = mMediaSwitchingController.getMediaItemList();
+        assertThat(items.get(0).isFirstDeviceInGroup()).isTrue();
+        assertThat(items.get(1).isFirstDeviceInGroup()).isFalse();
+    }
+
     private int getNumberOfConnectDeviceButtons() {
         int numberOfConnectDeviceButtons = 0;
         for (MediaItem item : mMediaSwitchingController.getMediaItemList()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index d59a404..0924df2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -52,7 +52,7 @@
 import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.views.NavigationBar;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -109,7 +109,7 @@
         MockitoAnnotations.initMocks(this);
         mNavigationBarController = spy(
                 new NavigationBarControllerImpl(mContext,
-                        mock(OverviewProxyService.class),
+                        mock(LauncherProxyService.class),
                         mock(NavigationModeController.class),
                         mock(SysUiState.class),
                         mCommandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
index a192446..50b8f37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
@@ -19,6 +19,7 @@
 import android.content.Intent
 import android.os.Handler
 import android.os.fakeExecutorHandler
+import android.platform.test.annotations.EnableFlags
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
 import android.telephony.telephonyManager
@@ -38,8 +39,7 @@
 import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogTransitionAnimator
-import com.android.systemui.flags.setFlagValue
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -62,6 +62,8 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper(setAsMainLooper = true)
+@EnableSceneContainer
+@EnableFlags(Flags.FLAG_QS_TILE_DETAILED_VIEW, Flags.FLAG_DUAL_SHADE)
 @UiThreadTest
 class InternetDetailsContentManagerTest : SysuiTestCase() {
     private val kosmos = Kosmos()
@@ -74,11 +76,8 @@
     private val internetDetailsContentController: InternetDetailsContentController =
         mock<InternetDetailsContentController>()
     private val keyguard: KeyguardStateController = mock<KeyguardStateController>()
-    private val dialogTransitionAnimator: DialogTransitionAnimator =
-        mock<DialogTransitionAnimator>()
     private val bgExecutor = FakeExecutor(FakeSystemClock())
     private lateinit var internetDetailsContentManager: InternetDetailsContentManager
-    private var subTitle: View? = null
     private var ethernet: LinearLayout? = null
     private var mobileDataLayout: LinearLayout? = null
     private var mobileToggleSwitch: Switch? = null
@@ -96,8 +95,6 @@
 
     @Before
     fun setUp() {
-        // TODO: b/377388104 enable this flag after integrating with details view.
-        mSetFlagsRule.setFlagValue(Flags.FLAG_QS_TILE_DETAILED_VIEW, false)
         whenever(telephonyManager.createForSubscriptionId(ArgumentMatchers.anyInt()))
             .thenReturn(telephonyManager)
         whenever(internetWifiEntry.title).thenReturn(WIFI_TITLE)
@@ -133,9 +130,7 @@
                 canConfigWifi = true,
                 coroutineScope = scope,
                 context = mContext,
-                internetDialog = null,
                 uiEventLogger = mock<UiEventLogger>(),
-                dialogTransitionAnimator = dialogTransitionAnimator,
                 handler = handler,
                 backgroundExecutor = bgExecutor,
                 keyguard = keyguard,
@@ -146,7 +141,6 @@
         internetDetailsContentManager.connectedWifiEntry = internetWifiEntry
         internetDetailsContentManager.wifiEntriesCount = wifiEntries.size
 
-        subTitle = contentView.requireViewById(R.id.internet_dialog_subtitle)
         ethernet = contentView.requireViewById(R.id.ethernet_layout)
         mobileDataLayout = contentView.requireViewById(R.id.mobile_network_layout)
         mobileToggleSwitch = contentView.requireViewById(R.id.mobile_toggle)
@@ -185,32 +179,6 @@
     }
 
     @Test
-    fun updateContent_withApmOn_internetDialogSubTitleGone() {
-        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
-        internetDetailsContentManager.updateContent(true)
-        bgExecutor.runAllReady()
-
-        internetDetailsContentManager.internetContentData.observe(
-            internetDetailsContentManager.lifecycleOwner!!
-        ) {
-            assertThat(subTitle!!.visibility).isEqualTo(View.VISIBLE)
-        }
-    }
-
-    @Test
-    fun updateContent_withApmOff_internetDialogSubTitleVisible() {
-        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
-        internetDetailsContentManager.updateContent(true)
-        bgExecutor.runAllReady()
-
-        internetDetailsContentManager.internetContentData.observe(
-            internetDetailsContentManager.lifecycleOwner!!
-        ) {
-            assertThat(subTitle!!.visibility).isEqualTo(View.VISIBLE)
-        }
-    }
-
-    @Test
     fun updateContent_apmOffAndHasEthernet_showEthernet() {
         whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
         whenever(internetDetailsContentController.hasEthernet()).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
similarity index 87%
rename from packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
index 4e1ccfb..69b762b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
@@ -40,11 +40,11 @@
 import com.android.systemui.navigationbar.NavigationBarController
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.process.ProcessWrapper
-import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP
+import com.android.systemui.recents.LauncherProxyService.ACTION_QUICKSTEP
 import com.android.systemui.settings.FakeDisplayTracker
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shade.ShadeViewController
-import com.android.systemui.shared.recents.IOverviewProxy
+import com.android.systemui.shared.recents.ILauncherProxy
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_MASK
 import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_ASLEEP
 import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_AWAKE
@@ -83,12 +83,12 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-class OverviewProxyServiceTest : SysuiTestCase() {
+class LauncherProxyServiceTest : SysuiTestCase() {
 
     @Main private val executor: Executor = MoreExecutors.directExecutor()
 
     private val kosmos = testKosmos()
-    private lateinit var subject: OverviewProxyService
+    private lateinit var subject: LauncherProxyService
     @Mock private val dumpManager = DumpManager()
     @Mock private val processWrapper = ProcessWrapper()
     private val displayTracker = FakeDisplayTracker(mContext)
@@ -97,10 +97,10 @@
     private val wakefulnessLifecycle =
         WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager)
 
-    @Mock private lateinit var overviewProxy: IOverviewProxy.Stub
+    @Mock private lateinit var launcherProxy: ILauncherProxy.Stub
     @Mock private lateinit var packageManager: PackageManager
 
-    // The following mocks belong to not-yet-tested parts of OverviewProxyService.
+    // The following mocks belong to not-yet-tested parts of LauncherProxyService.
     @Mock private lateinit var commandQueue: CommandQueue
     @Mock private lateinit var shellInterface: ShellInterface
     @Mock private lateinit var navBarController: NavigationBarController
@@ -127,18 +127,18 @@
         MockitoAnnotations.initMocks(this)
 
         val serviceComponent = ComponentName("test_package", "service_provider")
-        context.addMockService(serviceComponent, overviewProxy)
+        context.addMockService(serviceComponent, launcherProxy)
         context.addMockServiceResolver(
             TestableContext.MockServiceResolver {
                 if (it.action == ACTION_QUICKSTEP) serviceComponent else null
             }
         )
-        whenever(overviewProxy.queryLocalInterface(ArgumentMatchers.anyString()))
-            .thenReturn(overviewProxy)
-        whenever(overviewProxy.asBinder()).thenReturn(overviewProxy)
+        whenever(launcherProxy.queryLocalInterface(ArgumentMatchers.anyString()))
+            .thenReturn(launcherProxy)
+        whenever(launcherProxy.asBinder()).thenReturn(launcherProxy)
 
         // packageManager.resolveServiceAsUser has to return non-null for
-        // OverviewProxyService#isEnabled to become true.
+        // LauncherProxyService#isEnabled to become true.
         context.setMockPackageManager(packageManager)
         whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt()))
             .thenReturn(mock(ResolveInfo::class.java))
@@ -147,7 +147,7 @@
 
         // return isSystemUser as true by default.
         `when`(processWrapper.isSystemUser).thenReturn(true)
-        subject = createOverviewProxyService(context)
+        subject = createLauncherProxyService(context)
     }
 
     @After
@@ -159,11 +159,11 @@
     fun wakefulnessLifecycle_dispatchFinishedWakingUpSetsSysUIflagToAWAKE() {
         // WakefulnessLifecycle is initialized to AWAKE initially, and won't emit a noop.
         wakefulnessLifecycle.dispatchFinishedGoingToSleep()
-        clearInvocations(overviewProxy)
+        clearInvocations(launcherProxy)
 
         wakefulnessLifecycle.dispatchFinishedWakingUp()
 
-        verify(overviewProxy)
+        verify(launcherProxy)
             .onSystemUiStateChanged(
                 longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_AWAKE }
             )
@@ -173,7 +173,7 @@
     fun wakefulnessLifecycle_dispatchStartedWakingUpSetsSysUIflagToWAKING() {
         wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN)
 
-        verify(overviewProxy)
+        verify(launcherProxy)
             .onSystemUiStateChanged(
                 longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_WAKING }
             )
@@ -183,7 +183,7 @@
     fun wakefulnessLifecycle_dispatchFinishedGoingToSleepSetsSysUIflagToASLEEP() {
         wakefulnessLifecycle.dispatchFinishedGoingToSleep()
 
-        verify(overviewProxy)
+        verify(launcherProxy)
             .onSystemUiStateChanged(
                 longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_ASLEEP }
             )
@@ -195,56 +195,56 @@
             PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON
         )
 
-        verify(overviewProxy)
+        verify(launcherProxy)
             .onSystemUiStateChanged(
                 longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_GOING_TO_SLEEP }
             )
     }
 
     @Test
-    fun connectToOverviewService_primaryUserNoVisibleBgUsersSupported_expectBindService() {
+    fun connectToLauncherService_primaryUserNoVisibleBgUsersSupported_expectBindService() {
         `when`(processWrapper.isSystemUser).thenReturn(true)
         `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
         val spyContext = spy(context)
-        val ops = createOverviewProxyService(spyContext)
+        val ops = createLauncherProxyService(spyContext)
         ops.startConnectionToCurrentUser()
         verify(spyContext, atLeast(1)).bindServiceAsUser(any(), any(), anyInt(), any())
     }
 
     @Test
-    fun connectToOverviewService_nonPrimaryUserNoVisibleBgUsersSupported_expectNoBindService() {
+    fun connectToLauncherService_nonPrimaryUserNoVisibleBgUsersSupported_expectNoBindService() {
         `when`(processWrapper.isSystemUser).thenReturn(false)
         `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
         val spyContext = spy(context)
-        val ops = createOverviewProxyService(spyContext)
+        val ops = createLauncherProxyService(spyContext)
         ops.startConnectionToCurrentUser()
         verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
     }
 
     @Test
-    fun connectToOverviewService_nonPrimaryBgUserVisibleBgUsersSupported_expectBindService() {
+    fun connectToLauncherService_nonPrimaryBgUserVisibleBgUsersSupported_expectBindService() {
         `when`(processWrapper.isSystemUser).thenReturn(false)
         `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
         `when`(userManager.isUserForeground()).thenReturn(false)
         val spyContext = spy(context)
-        val ops = createOverviewProxyService(spyContext)
+        val ops = createLauncherProxyService(spyContext)
         ops.startConnectionToCurrentUser()
         verify(spyContext, atLeast(1)).bindServiceAsUser(any(), any(), anyInt(), any())
     }
 
     @Test
-    fun connectToOverviewService_nonPrimaryFgUserVisibleBgUsersSupported_expectNoBindService() {
+    fun connectToLauncherService_nonPrimaryFgUserVisibleBgUsersSupported_expectNoBindService() {
         `when`(processWrapper.isSystemUser).thenReturn(false)
         `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
         `when`(userManager.isUserForeground()).thenReturn(true)
         val spyContext = spy(context)
-        val ops = createOverviewProxyService(spyContext)
+        val ops = createLauncherProxyService(spyContext)
         ops.startConnectionToCurrentUser()
         verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
     }
 
-    private fun createOverviewProxyService(ctx: Context): OverviewProxyService {
-        return OverviewProxyService(
+    private fun createLauncherProxyService(ctx: Context): LauncherProxyService {
+        return LauncherProxyService(
             ctx,
             executor,
             commandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index e68153a..49d6909 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -57,7 +57,10 @@
 import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository
 import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
+import com.android.systemui.shade.data.repository.ShadeAnimationRepository
+import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
 import com.android.systemui.statusbar.BlurUtils
 import com.android.systemui.statusbar.DragDownHelper
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -219,6 +222,10 @@
                 notificationShadeDepthController,
                 view,
                 shadeViewController,
+                ShadeAnimationInteractorLegacyImpl(
+                    ShadeAnimationRepository(),
+                    ShadeRepositoryImpl(testScope),
+                ),
                 panelExpansionInteractor,
                 ShadeExpansionStateManager(),
                 stackScrollLayoutController,
@@ -247,6 +254,7 @@
                 mock(BouncerViewBinder::class.java),
                 { mock(ConfigurationForwarder::class.java) },
                 brightnessMirrorShowingInteractor,
+                kosmos.testDispatcher,
             )
         underTest.setupExpandedStatusBar()
         underTest.setDragDownHelper(dragDownHelper)
@@ -521,6 +529,18 @@
             verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
         }
 
+    @EnableFlags(Flags.FLAG_SHADE_LAUNCH_ACCESSIBILITY)
+    @Test
+    fun notifiesTheViewWhenLaunchAnimationIsRunning() {
+        testScope.runTest {
+            underTest.setExpandAnimationRunning(true)
+            verify(view).setAnimatingContentLaunch(true)
+
+            underTest.setExpandAnimationRunning(false)
+            verify(view).setAnimatingContentLaunch(false)
+        }
+    }
+
     @Test
     @DisableSceneContainer
     fun setsUpCommunalHubLayout_whenFlagEnabled() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index a04ca03..9abe9aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -32,8 +32,8 @@
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
 import com.android.systemui.plugins.qs.QS
-import com.android.systemui.recents.OverviewProxyService
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -71,7 +71,7 @@
 
     private val view = mock<NotificationsQuickSettingsContainer>()
     private val navigationModeController = mock<NavigationModeController>()
-    private val overviewProxyService = mock<OverviewProxyService>()
+    private val mLauncherProxyService = mock<LauncherProxyService>()
     private val shadeHeaderController = mock<ShadeHeaderController>()
     private val shadeInteractor = mock<ShadeInteractor>()
     private val fragmentService = mock<FragmentService>()
@@ -81,7 +81,7 @@
     private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>()
 
     @Captor lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
-    @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener>
+    @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<LauncherProxyListener>
     @Captor lateinit var windowInsetsCallbackCaptor: ArgumentCaptor<Consumer<WindowInsets>>
     @Captor lateinit var constraintSetCaptor: ArgumentCaptor<ConstraintSet>
     @Captor lateinit var attachStateListenerCaptor: ArgumentCaptor<View.OnAttachStateChangeListener>
@@ -89,7 +89,7 @@
     lateinit var underTest: NotificationsQSContainerController
 
     private lateinit var navigationModeCallback: ModeChangedListener
-    private lateinit var taskbarVisibilityCallback: OverviewProxyListener
+    private lateinit var taskbarVisibilityCallback: LauncherProxyListener
     private lateinit var windowInsetsCallback: Consumer<WindowInsets>
     private lateinit var fakeSystemClock: FakeSystemClock
     private lateinit var delayableExecutor: FakeExecutor
@@ -110,7 +110,7 @@
             NotificationsQSContainerController(
                 view,
                 navigationModeController,
-                overviewProxyService,
+                mLauncherProxyService,
                 shadeHeaderController,
                 shadeInteractor,
                 fragmentService,
@@ -127,7 +127,7 @@
         overrideResource(R.dimen.qs_footer_action_inset, FOOTER_ACTIONS_INSET)
         whenever(navigationModeController.addListener(navigationModeCaptor.capture()))
             .thenReturn(GESTURES_NAVIGATION)
-        doNothing().`when`(overviewProxyService).addCallback(taskbarVisibilityCaptor.capture())
+        doNothing().`when`(mLauncherProxyService).addCallback(taskbarVisibilityCaptor.capture())
         doNothing().`when`(view).setInsetsChangedListener(windowInsetsCallbackCaptor.capture())
         doNothing().`when`(view).applyConstraints(constraintSetCaptor.capture())
         doNothing().`when`(view).addOnAttachStateChangeListener(attachStateListenerCaptor.capture())
@@ -402,7 +402,7 @@
             NotificationsQSContainerController(
                 container,
                 navigationModeController,
-                overviewProxyService,
+                mLauncherProxyService,
                 shadeHeaderController,
                 shadeInteractor,
                 fragmentService,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index 24f8843..4c12cc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -32,8 +32,8 @@
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
 import com.android.systemui.plugins.qs.QS
-import com.android.systemui.recents.OverviewProxyService
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -70,7 +70,7 @@
 
     private val view = mock<NotificationsQuickSettingsContainer>()
     private val navigationModeController = mock<NavigationModeController>()
-    private val overviewProxyService = mock<OverviewProxyService>()
+    private val mLauncherProxyService = mock<LauncherProxyService>()
     private val shadeHeaderController = mock<ShadeHeaderController>()
     private val shadeInteractor = mock<ShadeInteractor>()
     private val fragmentService = mock<FragmentService>()
@@ -80,7 +80,7 @@
     private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>()
 
     @Captor lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
-    @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener>
+    @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<LauncherProxyListener>
     @Captor lateinit var windowInsetsCallbackCaptor: ArgumentCaptor<Consumer<WindowInsets>>
     @Captor lateinit var constraintSetCaptor: ArgumentCaptor<ConstraintSet>
     @Captor lateinit var attachStateListenerCaptor: ArgumentCaptor<View.OnAttachStateChangeListener>
@@ -88,7 +88,7 @@
     lateinit var underTest: NotificationsQSContainerController
 
     private lateinit var navigationModeCallback: ModeChangedListener
-    private lateinit var taskbarVisibilityCallback: OverviewProxyListener
+    private lateinit var taskbarVisibilityCallback: LauncherProxyListener
     private lateinit var windowInsetsCallback: Consumer<WindowInsets>
     private lateinit var fakeSystemClock: FakeSystemClock
     private lateinit var delayableExecutor: FakeExecutor
@@ -110,7 +110,7 @@
             NotificationsQSContainerController(
                 view,
                 navigationModeController,
-                overviewProxyService,
+                mLauncherProxyService,
                 shadeHeaderController,
                 shadeInteractor,
                 fragmentService,
@@ -127,7 +127,7 @@
         overrideResource(R.dimen.qs_footer_action_inset, FOOTER_ACTIONS_INSET)
         whenever(navigationModeController.addListener(navigationModeCaptor.capture()))
             .thenReturn(GESTURES_NAVIGATION)
-        doNothing().`when`(overviewProxyService).addCallback(taskbarVisibilityCaptor.capture())
+        doNothing().`when`(mLauncherProxyService).addCallback(taskbarVisibilityCaptor.capture())
         doNothing().`when`(view).setInsetsChangedListener(windowInsetsCallbackCaptor.capture())
         doNothing().`when`(view).applyConstraints(constraintSetCaptor.capture())
         doNothing().`when`(view).addOnAttachStateChangeListener(attachStateListenerCaptor.capture())
@@ -457,7 +457,7 @@
             NotificationsQSContainerController(
                 container,
                 navigationModeController,
-                overviewProxyService,
+                mLauncherProxyService,
                 shadeHeaderController,
                 shadeInteractor,
                 fragmentService,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index eae8285..edb0f35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -53,6 +53,7 @@
 import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
 import com.android.systemui.shade.carrier.ShadeCarrierGroup
 import com.android.systemui.shade.carrier.ShadeCarrierGroupController
+import com.android.systemui.shade.data.repository.shadeDisplaysRepository
 import com.android.systemui.statusbar.data.repository.fakeStatusBarContentInsetsProviderStore
 import com.android.systemui.statusbar.phone.StatusIconContainer
 import com.android.systemui.statusbar.phone.StatusOverlayHoverListenerFactory
@@ -70,6 +71,7 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -96,7 +98,7 @@
 
     private val kosmos = testKosmos()
     private val insetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore
-    private val insetsProvider = insetsProviderStore.defaultDisplay
+    private val insetsProvider = insetsProviderStore.forDisplay(context.displayId)
 
     @Mock(answer = Answers.RETURNS_MOCKS) private lateinit var view: MotionLayout
     @Mock private lateinit var statusIcons: StatusIconContainer
@@ -196,6 +198,8 @@
                 privacyIconsController,
                 insetsProviderStore,
                 configurationController,
+                viewContext,
+                Lazy { kosmos.shadeDisplaysRepository },
                 variableDateViewControllerFactory,
                 batteryMeterViewController,
                 dumpManager,
@@ -294,7 +298,7 @@
 
         verify(clock).setTextAppearance(R.style.TextAppearance_QS_Status)
         verify(date).setTextAppearance(R.style.TextAppearance_QS_Status)
-        verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
+        verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status)
     }
 
     @Test
@@ -809,6 +813,43 @@
     }
 
     @Test
+    fun sameInsetsTwice_listenerCallsOnApplyWindowInsetsOnlyOnce() {
+        val windowInsets = createWindowInsets()
+
+        val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+        verify(view).setOnApplyWindowInsetsListener(capture(captor))
+
+        val listener = captor.value
+
+        listener.onApplyWindowInsets(view, windowInsets)
+
+        verify(view, times(1)).onApplyWindowInsets(any())
+
+        listener.onApplyWindowInsets(view, windowInsets)
+
+        verify(view, times(1)).onApplyWindowInsets(any())
+    }
+
+    @Test
+    fun twoDifferentInsets_listenerCallsOnApplyWindowInsetsTwice() {
+        val windowInsets1 = WindowInsets(Rect(1, 2, 3, 4))
+        val windowInsets2 = WindowInsets(Rect(5, 6, 7, 8))
+
+        val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+        verify(view).setOnApplyWindowInsetsListener(capture(captor))
+
+        val listener = captor.value
+
+        listener.onApplyWindowInsets(view, windowInsets1)
+
+        verify(view, times(1)).onApplyWindowInsets(any())
+
+        listener.onApplyWindowInsets(view, windowInsets2)
+
+        verify(view, times(2)).onApplyWindowInsets(any())
+    }
+
+    @Test
     fun alarmIconNotIgnored() {
         verify(statusIcons, Mockito.never())
             .addIgnoredSlot(context.getString(com.android.internal.R.string.status_bar_alarm_clock))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
index b3a5872..57c2858 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
@@ -19,11 +19,11 @@
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.clocks.ClockLogger
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockSettings
 import com.android.systemui.shared.clocks.ClockContext
 import com.android.systemui.shared.clocks.FontTextStyle
-import com.android.systemui.shared.clocks.LogUtil
 import com.android.systemui.shared.clocks.TypefaceCache
 import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
 import org.junit.Assert.assertEquals
@@ -34,7 +34,7 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class SimpleDigitalClockTextViewTest : SysuiTestCase() {
-    private val messageBuffer = LogUtil.DEBUG_MESSAGE_BUFFER
+    private val messageBuffer = ClockLogger.DEBUG_MESSAGE_BUFFER
     private lateinit var underTest: SimpleDigitalClockTextView
     private val defaultLargeClockTextSize = 500F
     private val smallerTextSize = 300F
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index ce50580..77b116e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -69,6 +69,7 @@
 import com.android.systemui.statusbar.notification.SourceType;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
 import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
@@ -369,7 +370,7 @@
 
         row.onDensityOrFontScaleChanged();
 
-        verify(mockContainer).reInflateViews(any(), any());
+        verify(mockContainer).reInflateViews(any());
     }
 
     @Test
@@ -878,6 +879,85 @@
     }
 
     @Test
+    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    public void isExpanded_sensitivePromotedNotification_notExpanded() throws Exception {
+        // GIVEN
+        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        row.setPromotedOngoing(true);
+        row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
+        row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true);
+
+        // THEN
+        assertThat(row.isExpanded()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    public void isExpanded_promotedNotificationNotOnKeyguard_expanded() throws Exception {
+        // GIVEN
+        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        row.setPromotedOngoing(true);
+        row.setOnKeyguard(false);
+
+        // THEN
+        assertThat(row.isExpanded()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    public void isExpanded_promotedNotificationAllowOnKeyguard_expanded() throws Exception {
+        // GIVEN
+        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        row.setPromotedOngoing(true);
+        row.setOnKeyguard(true);
+
+        // THEN
+        assertThat(row.isExpanded(/* allowOnKeyguard = */ true)).isTrue();
+    }
+
+    @Test
+    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    public void isExpanded_promotedNotificationIgnoreLockscreenConstraints_expanded()
+            throws Exception {
+        // GIVEN
+        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        row.setPromotedOngoing(true);
+        row.setOnKeyguard(true);
+        row.setIgnoreLockscreenConstraints(true);
+
+        // THEN
+        assertThat(row.isExpanded()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    public void isExpanded_promotedNotificationSaveSpaceOnLockScreen_notExpanded()
+            throws Exception {
+        // GIVEN
+        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        row.setPromotedOngoing(true);
+        row.setOnKeyguard(true);
+        row.setSaveSpaceOnLockscreen(true);
+
+        // THEN
+        assertThat(row.isExpanded()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    public void isExpanded_promotedNotificationNotSaveSpaceOnLockScreen_expanded()
+            throws Exception {
+        // GIVEN
+        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        row.setPromotedOngoing(true);
+        row.setOnKeyguard(true);
+        row.setSaveSpaceOnLockscreen(false);
+
+        // THEN
+        assertThat(row.isExpanded()).isTrue();
+    }
+
+    @Test
     public void onDisappearAnimationFinished_shouldSetFalse_headsUpAnimatingAway()
             throws Exception {
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 61943f2..8645a40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -444,6 +444,7 @@
                 eq(true), /* wasShownHighPriority */
                 eq(assistantFeedbackController),
                 any<MetricsLogger>(),
+                any<View.OnClickListener>(),
             )
     }
 
@@ -476,6 +477,7 @@
                 eq(false), /* wasShownHighPriority */
                 eq(assistantFeedbackController),
                 any<MetricsLogger>(),
+                any<View.OnClickListener>(),
             )
     }
 
@@ -508,6 +510,7 @@
                 eq(false), /* wasShownHighPriority */
                 eq(assistantFeedbackController),
                 any<MetricsLogger>(),
+                any<View.OnClickListener>(),
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
index 1eb88c5..0457255 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
@@ -19,10 +19,12 @@
 import android.app.Person
 import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
+import android.view.View.GONE
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.RankingBuilder
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE
 import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.inflatePrivateSingleLineView
@@ -90,6 +92,7 @@
                 builder = notificationBuilder,
                 systemUiContext = context,
                 redactText = false,
+                summarization = null
             )
 
         // WHEN: binds the viewHolder
@@ -151,6 +154,7 @@
                 builder = notificationBuilder,
                 systemUiContext = context,
                 redactText = false,
+                summarization = null
             )
         // WHEN: binds the view
         SingleLineViewBinder.bind(viewModel, view)
@@ -200,6 +204,7 @@
                 builder = notificationBuilder,
                 systemUiContext = context,
                 redactText = false,
+                summarization = null
             )
         // WHEN: binds the view with the view model
         SingleLineViewBinder.bind(viewModel, view)
@@ -211,6 +216,70 @@
         assertNull(viewModel.conversationData)
     }
 
+    @Test
+    @EnableFlags(AsyncHybridViewInflation.FLAG_NAME, android.app.Flags.FLAG_NM_SUMMARIZATION_UI,
+        android.app.Flags.FLAG_NM_SUMMARIZATION)
+    fun bindSummarizedGroupConversationSingleLineView() {
+        // GIVEN a row with a group conversation notification
+        val user =
+            Person.Builder()
+                .setName(USER_NAME)
+                .build()
+        val style =
+            Notification.MessagingStyle(user)
+                .addMessage(MESSAGE_TEXT, System.currentTimeMillis(), user)
+                .addMessage(
+                    "How about lunch?",
+                    System.currentTimeMillis(),
+                    Person.Builder().setName("user2").build(),
+                )
+                .setGroupConversation(true)
+        notificationBuilder.setStyle(style).setShortcutId(SHORTCUT_ID)
+        val notification = notificationBuilder.build()
+        val row = helper.createRow(notification)
+        val rb = RankingBuilder(row.entry.ranking)
+        rb.setSummarization("summary!")
+        row.entry.ranking = rb.build()
+
+        val view =
+            inflatePrivateSingleLineView(
+                isConversation = true,
+                reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE,
+                entry = row.entry,
+                context = context,
+                logger = mock(),
+            )
+                    as HybridConversationNotificationView
+
+        val publicView =
+            inflatePublicSingleLineView(
+                isConversation = true,
+                reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE,
+                entry = row.entry,
+                context = context,
+                logger = mock(),
+            )
+                    as HybridConversationNotificationView
+        assertNotNull(publicView)
+
+        val viewModel =
+            SingleLineViewInflater.inflateSingleLineViewModel(
+                notification = notification,
+                messagingStyle = style,
+                builder = notificationBuilder,
+                systemUiContext = context,
+                redactText = false,
+                summarization = "summary"
+            )
+        // WHEN: binds the view
+        SingleLineViewBinder.bind(viewModel, view)
+
+        // THEN: the single-line conversation view should only include summarization content
+        assertEquals(viewModel.conversationData?.summarization, view.textView.text)
+        assertEquals("", view.conversationSenderNameView.text)
+        assertEquals(GONE, view.conversationSenderNameView.visibility)
+    }
+
     private companion object {
         const val CHANNEL_ID = "CHANNEL_ID"
         const val CONTENT_TITLE = "A Cool New Feature"
@@ -218,5 +287,6 @@
         const val USER_NAME = "USER_NAME"
         const val MESSAGE_TEXT = "MESSAGE_TEXT"
         const val SHORTCUT_ID = "Shortcut"
+        const val SUMMARIZATION = "summarization"
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
index ef70e27..13724a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
@@ -272,6 +272,35 @@
         }
     }
 
+    @Test
+    fun createViewModelForSummarizedConversationNotification() {
+        // Given: a non-group conversation notification
+        val notificationType = OneToOneConversation()
+        val notification = getNotification(notificationType)
+
+        // When: inflate the SingleLineViewModel
+        val singleLineViewModel = notification.makeSingleLineViewModel(notificationType)
+
+        // Then: the inflated SingleLineViewModel should be as expected
+        // titleText: Notification.ConversationTitle
+        // contentText: the last message text
+        // conversationSenderName: null, because it's not a group conversation
+        // conversationData.avatar: a single icon of the last sender
+        // summarizedText: the summary text from the ranking
+        assertEquals(CONVERSATION_TITLE, singleLineViewModel.titleText)
+        assertEquals(LAST_MESSAGE, singleLineViewModel.contentText)
+        assertNull(
+            singleLineViewModel.conversationData?.conversationSenderName,
+            "Sender name should be null for one-on-one conversation"
+        )
+        assertTrue {
+            singleLineViewModel.conversationData
+                ?.avatar
+                ?.equalsTo(SingleIcon(firstSenderIcon.loadDrawable(context))) == true
+        }
+        assertEquals("summary", singleLineViewModel.conversationData?.summarization)
+    }
+
     sealed class NotificationType(val largeIcon: Icon? = null)
 
     class NonMessaging(largeIcon: Icon? = null) : NotificationType(largeIcon)
@@ -380,7 +409,8 @@
             if (isConversation) messagingStyle else null,
             builder,
             context,
-            false
+            false,
+            "summary"
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 437ccb6..68f6661 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -90,6 +90,8 @@
     private val kosmos = testKosmos()
     private val statusBarContentInsetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore
     private val statusBarContentInsetsProvider = statusBarContentInsetsProviderStore.defaultDisplay
+    private val statusBarContentInsetsProviderForSecondaryDisplay =
+        statusBarContentInsetsProviderStore.forDisplay(SECONDARY_DISPLAY_ID)
 
     private val fakeDarkIconDispatcher = kosmos.fakeDarkIconDispatcher
     @Mock private lateinit var shadeViewController: ShadeViewController
@@ -144,6 +146,12 @@
             controller = createAndInitController(view)
         }
 
+        `when`(
+                statusBarContentInsetsProviderForSecondaryDisplay
+                    .getStatusBarContentInsetsForCurrentRotation()
+            )
+            .thenReturn(Insets.NONE)
+
         val contextForSecondaryDisplay =
             SysuiTestableContext(
                 mContext.createDisplayContext(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index a02d333..a7fe1ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -59,6 +59,7 @@
 import com.android.keyguard.BouncerPanelExpansionCalculator;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.DejankUtils;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
@@ -71,6 +72,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.transitions.BlurConfig;
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
@@ -110,6 +112,9 @@
 @RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
+// TODO(b/381263619) there are more changes and tweaks required to match the new bouncer/shade specs
+// Disabling for now but it will be fixed before the flag is fully ramped up.
+@DisableFlags(Flags.FLAG_BOUNCER_UI_REVAMP)
 public class ScrimControllerTest extends SysuiTestCase {
 
     @Rule public Expect mExpect = Expect.create();
@@ -286,7 +291,8 @@
                 mKeyguardTransitionInteractor,
                 mKeyguardInteractor,
                 mKosmos.getTestDispatcher(),
-                mLinearLargeScreenShadeInterpolator);
+                mLinearLargeScreenShadeInterpolator,
+                new BlurConfig(0.0f, 0.0f));
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
         mScrimController.setAnimatorListener(mAnimatorListener);
@@ -1251,7 +1257,8 @@
                 mKeyguardTransitionInteractor,
                 mKeyguardInteractor,
                 mKosmos.getTestDispatcher(),
-                mLinearLargeScreenShadeInterpolator);
+                mLinearLargeScreenShadeInterpolator,
+                new BlurConfig(0.0f, 0.0f));
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
         mScrimController.setAnimatorListener(mAnimatorListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fab7922..5d88f72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -98,6 +98,7 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
@@ -2894,6 +2895,12 @@
         @Override
         public void animateBubbleBarLocation(BubbleBarLocation location) {
         }
+
+        @Override
+        public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation location) {}
+
+        @Override
+        public void onItemDraggedOutsideBubbleBarDropZone() {}
     }
 
     private static class FakeBubbleProperties implements BubbleProperties {
diff --git a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
index de4bbec..42c509e 100644
--- a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
+++ b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
@@ -16,16 +16,19 @@
 
 package android.hardware.input
 
+import android.hardware.input.InputGestureData.Trigger
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
 import android.hardware.input.InputManager.InputDeviceListener
 import android.view.InputDevice
 import android.view.KeyCharacterMap
 import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD
 import android.view.KeyEvent
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.invocation.InvocationOnMock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
 
 class FakeInputManager {
 
@@ -49,36 +52,79 @@
         )
 
     private var inputDeviceListener: InputDeviceListener? = null
+    private val customInputGestures: MutableMap<Trigger, InputGestureData> = mutableMapOf()
+    var addCustomInputGestureErrorCode = CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS
 
-    val inputManager =
-        mock<InputManager> {
-            whenever(getInputDevice(anyInt())).thenAnswer { invocation ->
+    val inputManager: InputManager = mock {
+        on { getCustomInputGestures(any()) }.then { customInputGestures.values.toList() }
+
+        on { addCustomInputGesture(any()) }
+            .then {
+                val inputGestureData = it.getArgument<InputGestureData>(0)
+                val trigger = inputGestureData.trigger
+
+                if (customInputGestures.containsKey(trigger)) {
+                    addCustomInputGestureErrorCode
+                } else {
+                    customInputGestures[trigger] = inputGestureData
+                    CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+                }
+            }
+
+        on { removeCustomInputGesture(any()) }
+            .then {
+                val inputGestureData = it.getArgument<InputGestureData>(0)
+                val trigger = inputGestureData.trigger
+
+                if (customInputGestures.containsKey(trigger)) {
+                    customInputGestures.remove(trigger)
+                    CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+                } else {
+                    CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
+                }
+            }
+
+        on { removeAllCustomInputGestures(any()) }.then { customInputGestures.clear() }
+
+        on { getInputGesture(any()) }
+            .then {
+                val trigger = it.getArgument<Trigger>(0)
+                customInputGestures[trigger]
+            }
+
+        on { getInputDevice(anyInt()) }
+            .thenAnswer { invocation ->
                 val deviceId = invocation.arguments[0] as Int
                 return@thenAnswer devices[deviceId]
             }
-            whenever(inputDeviceIds).thenAnswer {
+        on { inputDeviceIds }
+            .thenAnswer {
                 return@thenAnswer devices.keys.toIntArray()
             }
 
-            fun setDeviceEnabled(invocation: InvocationOnMock, enabled: Boolean) {
-                val deviceId = invocation.arguments[0] as Int
-                val device = devices[deviceId] ?: return
-                devices[deviceId] = device.copy(enabled = enabled)
-            }
+        fun setDeviceEnabled(invocation: InvocationOnMock, enabled: Boolean) {
+            val deviceId = invocation.arguments[0] as Int
+            val device = devices[deviceId] ?: return
+            devices[deviceId] = device.copy(enabled = enabled)
+        }
 
-            whenever(disableInputDevice(anyInt())).thenAnswer { invocation ->
-                setDeviceEnabled(invocation, enabled = false)
-            }
-            whenever(enableInputDevice(anyInt())).thenAnswer { invocation ->
-                setDeviceEnabled(invocation, enabled = true)
-            }
-            whenever(deviceHasKeys(any(), any())).thenAnswer { invocation ->
+        on { disableInputDevice(anyInt()) }
+            .thenAnswer { invocation -> setDeviceEnabled(invocation, enabled = false) }
+        on { enableInputDevice(anyInt()) }
+            .thenAnswer { invocation -> setDeviceEnabled(invocation, enabled = true) }
+        on { deviceHasKeys(any(), any()) }
+            .thenAnswer { invocation ->
                 val deviceId = invocation.arguments[0] as Int
                 val keyCodes = invocation.arguments[1] as IntArray
                 val supportedKeyCodes = supportedKeyCodesByDeviceId[deviceId]!!
                 return@thenAnswer keyCodes.map { supportedKeyCodes.contains(it) }.toBooleanArray()
             }
-        }
+    }
+
+    fun resetCustomInputGestures() {
+        customInputGestures.clear()
+        addCustomInputGestureErrorCode = CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS
+    }
 
     fun addPhysicalKeyboardIfNotPresent(deviceId: Int, enabled: Boolean = true) {
         if (devices.containsKey(deviceId)) {
@@ -97,7 +143,7 @@
         vendorId: Int = 0,
         productId: Int = 0,
         isFullKeyboard: Boolean = true,
-        enabled: Boolean = true
+        enabled: Boolean = true,
     ) {
         check(id > 0) { "Physical keyboard ids have to be > 0" }
         addKeyboard(id, vendorId, productId, isFullKeyboard, enabled)
@@ -113,7 +159,7 @@
         vendorId: Int = 0,
         productId: Int = 0,
         isFullKeyboard: Boolean = true,
-        enabled: Boolean = true
+        enabled: Boolean = true,
     ) {
         val keyboardType =
             if (isFullKeyboard) InputDevice.KEYBOARD_TYPE_ALPHABETIC
@@ -152,7 +198,7 @@
         id: Int = getId(),
         type: Int = keyboardType,
         sources: Int = getSources(),
-        enabled: Boolean = isEnabled
+        enabled: Boolean = isEnabled,
     ) =
         InputDevice.Builder()
             .setId(id)
diff --git a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
index 7ed7361..76fc611 100644
--- a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
+++ b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
@@ -433,10 +433,6 @@
 
     override fun showRearDisplayDialog(currentBaseState: Int) {}
 
-    override fun unbundleNotification(key: String) {}
-
-    override fun rebundleNotification(key: String) {}
-
     companion object {
         const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
         const val SECONDARY_DISPLAY_ID = 2
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
index 2bd104d..48b801c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.user.domain.interactor.selectedUserInteractor
 
 val Kosmos.authenticationInteractor by
@@ -29,5 +30,6 @@
             backgroundDispatcher = testDispatcher,
             repository = authenticationRepository,
             selectedUserInteractor = selectedUserInteractor,
+            tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
         )
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/BatteryRepositoryKosmos.kt
similarity index 64%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/BatteryRepositoryKosmos.kt
index aa262f9..edfe8ec 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/BatteryRepositoryKosmos.kt
@@ -14,14 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.utils
+package com.android.systemui.common.data.repository
 
-import android.app.Instrumentation
+import com.android.systemui.kosmos.Kosmos
 
-object RecentTasksUtils {
-    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
-        instrumentation.uiAutomation.executeShellCommand(
-            "dumpsys activity service SystemUIService WMShell recents clearAll"
-        )
-    }
-}
\ No newline at end of file
+val Kosmos.batteryRepository: BatteryRepository by Kosmos.Fixture { FakeBatteryRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakeBatteryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakeBatteryRepository.kt
new file mode 100644
index 0000000..ac94335
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakeBatteryRepository.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 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.common.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeBatteryRepository : BatteryRepository {
+    private val _isDevicePluggedIn = MutableStateFlow(false)
+
+    override val isDevicePluggedIn: Flow<Boolean> = _isDevicePluggedIn.asStateFlow()
+
+    fun setDevicePluggedIn(isPluggedIn: Boolean) {
+        _isDevicePluggedIn.value = isPluggedIn
+    }
+}
+
+val BatteryRepository.fake
+    get() = this as FakeBatteryRepository
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/BatteryInteractorKosmos.kt
similarity index 64%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/BatteryInteractorKosmos.kt
index aa262f9..2153955 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/BatteryInteractorKosmos.kt
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.utils
+package com.android.systemui.common.domain.interactor
 
-import android.app.Instrumentation
+import com.android.systemui.common.data.repository.batteryRepository
+import com.android.systemui.kosmos.Kosmos
 
-object RecentTasksUtils {
-    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
-        instrumentation.uiAutomation.executeShellCommand(
-            "dumpsys activity service SystemUIService WMShell recents clearAll"
-        )
-    }
-}
\ No newline at end of file
+var Kosmos.batteryInteractor by Kosmos.Fixture { BatteryInteractor(batteryRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
index 5da6c7b..ea0dc6c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
@@ -18,12 +18,23 @@
 
 import android.app.admin.devicePolicyManager
 import android.content.res.mainResources
+import com.android.systemui.Flags
 import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.flags.featureFlagsClassic
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.util.settings.fakeSettings
 
+val Kosmos.communalDefaultBackground: CommunalBackgroundType by
+    Kosmos.Fixture {
+        if (Flags.glanceableHubBlurredBackground()) {
+            CommunalBackgroundType.BLUR
+        } else {
+            CommunalBackgroundType.ANIMATED
+        }
+    }
+
 val Kosmos.communalSettingsRepository: CommunalSettingsRepository by
     Kosmos.Fixture {
         CommunalSettingsRepositoryImpl(
@@ -33,5 +44,6 @@
             secureSettings = fakeSettings,
             broadcastDispatcher = broadcastDispatcher,
             devicePolicyManager = devicePolicyManager,
+            defaultBackgroundType = communalDefaultBackground,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
index 1636257..603160d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
@@ -25,7 +25,8 @@
 /** Fake implementation of [CommunalPrefsRepository] */
 class FakeCommunalPrefsRepository : CommunalPrefsRepository {
     private val _isCtaDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
-    private val _isHubOnboardingismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
+    private val _isHubOnboardingDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
+    private val _isDreamButtonTooltipDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
 
     override fun isCtaDismissed(user: UserInfo): Flow<Boolean> =
         _isCtaDismissed.map { it.contains(user) }
@@ -35,10 +36,18 @@
     }
 
     override fun isHubOnboardingDismissed(user: UserInfo): Flow<Boolean> =
-        _isHubOnboardingismissed.map { it.contains(user) }
+        _isHubOnboardingDismissed.map { it.contains(user) }
 
     override suspend fun setHubOnboardingDismissed(user: UserInfo) {
-        _isHubOnboardingismissed.value =
-            _isHubOnboardingismissed.value.toMutableSet().apply { add(user) }
+        _isHubOnboardingDismissed.value =
+            _isHubOnboardingDismissed.value.toMutableSet().apply { add(user) }
+    }
+
+    override fun isDreamButtonTooltipDismissed(user: UserInfo): Flow<Boolean> =
+        _isDreamButtonTooltipDismissed.map { it.contains(user) }
+
+    override suspend fun setDreamButtonTooltipDismissed(user: UserInfo) {
+        _isDreamButtonTooltipDismissed.value =
+            _isDreamButtonTooltipDismissed.value.toMutableSet().apply { add(user) }
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 89aad4b..b0a6de1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -19,10 +19,13 @@
 import android.content.testableContext
 import android.os.userManager
 import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.common.domain.interactor.batteryInteractor
 import com.android.systemui.communal.data.repository.communalMediaRepository
 import com.android.systemui.communal.data.repository.communalSmartspaceRepository
 import com.android.systemui.communal.data.repository.communalWidgetRepository
+import com.android.systemui.communal.posturing.domain.interactor.posturingInteractor
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
+import com.android.systemui.dock.dockManager
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -64,6 +67,9 @@
         logBuffer = logcatLogBuffer("CommunalInteractor"),
         tableLogBuffer = mock(),
         managedProfileController = fakeManagedProfileController,
+        batteryInteractor = batteryInteractor,
+        dockManager = dockManager,
+        posturingInteractor = posturingInteractor,
     )
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
index c2d2392..43d3eb7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
@@ -18,6 +18,7 @@
 
 import android.service.dream.dreamManager
 import com.android.internal.logging.uiEventLogger
+import com.android.systemui.communal.domain.interactor.communalPrefsInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
@@ -29,6 +30,7 @@
         CommunalToDreamButtonViewModel(
             backgroundContext = testDispatcher,
             batteryController = batteryController,
+            prefsInteractor = communalPrefsInteractor,
             settingsInteractor = communalSettingsInteractor,
             activityStarter = activityStarter,
             dreamManager = dreamManager,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
index 1d3fd30..c927b55 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.keyguard.dismissCallbackRegistry
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.scene.domain.interactor.sceneBackInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 
@@ -36,5 +37,6 @@
             alternateBouncerInteractor = alternateBouncerInteractor,
             dismissCallbackRegistry = dismissCallbackRegistry,
             sceneBackInteractor = sceneBackInteractor,
+            tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
index e4c7df6..9e36428 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
 
@@ -40,6 +41,7 @@
             systemPropertiesHelper = fakeSystemPropertiesHelper,
             userAwareSecureSettingsRepository = userAwareSecureSettingsRepository,
             keyguardInteractor = keyguardInteractor,
+            tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
         )
         .apply { activateIn(testScope) }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeFocusedDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeFocusedDisplayRepository.kt
index 83df5d8..ad9370f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeFocusedDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeFocusedDisplayRepository.kt
@@ -33,7 +33,7 @@
     override val focusedDisplayId: StateFlow<Int>
         get() = flow.asStateFlow()
 
-    suspend fun emit(focusedDisplay: Int) = flow.emit(focusedDisplay)
+    suspend fun setDisplayId(focusedDisplay: Int) = flow.emit(focusedDisplay)
 }
 
 @Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
index 93e7f2e..83f4e8f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.recents.LauncherProxyService
 import com.android.systemui.touchpad.data.repository.touchpadRepository
 import com.android.systemui.user.data.repository.userRepository
 import org.mockito.kotlin.mock
@@ -43,12 +43,12 @@
                     userRepository,
                 ),
             tutorialRepository = tutorialSchedulerRepository,
-            overviewProxyService = mockOverviewProxyService,
+            launcherProxyService = mockLauncherProxyService,
             metricsLogger = mockEduMetricsLogger,
             clock = fakeEduClock,
         )
     }
 
 var Kosmos.mockEduMetricsLogger by Kosmos.Fixture { mock<ContextualEducationMetricsLogger>() }
-var Kosmos.mockOverviewProxyService by Kosmos.Fixture { mock<OverviewProxyService>() }
+var Kosmos.mockLauncherProxyService by Kosmos.Fixture { mock<LauncherProxyService>() }
 var Kosmos.mockEduInputManager by Kosmos.Fixture { mock<InputManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 0192fa4..739f6c2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -79,7 +79,7 @@
 var Kosmos.shortcutHelperCurrentAppShortcutsSource: KeyboardShortcutGroupsSource by
     Kosmos.Fixture { CurrentAppShortcutsSource(windowManager) }
 
-val Kosmos.shortcutHelperAccessibilityShortcutsSource: KeyboardShortcutGroupsSource by
+var Kosmos.shortcutHelperAccessibilityShortcutsSource: KeyboardShortcutGroupsSource by
     Kosmos.Fixture { AccessibilityShortcutsSource(mainResources) }
 
 val Kosmos.shortcutHelperExclusions by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 8489d83..8ea80081 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.graphics.Point
+import android.graphics.RectF
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
@@ -129,6 +130,10 @@
     override val notificationStackAbsoluteBottom: StateFlow<Float>
         get() = _notificationStackAbsoluteBottom.asStateFlow()
 
+    private val _wallpaperFocalAreaBounds = MutableStateFlow(RectF(0f, 0f, 0f, 0f))
+    override val wallpaperFocalAreaBounds: StateFlow<RectF>
+        get() = _wallpaperFocalAreaBounds.asStateFlow()
+
     private val _isKeyguardEnabled = MutableStateFlow(true)
     override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow()
 
@@ -287,6 +292,10 @@
         _notificationStackAbsoluteBottom.value = bottom
     }
 
+    override fun setWallpaperFocalAreaBounds(bounds: RectF) {
+        _wallpaperFocalAreaBounds.value = bounds
+    }
+
     override fun setCanIgnoreAuthAndReturnToGone(canWake: Boolean) {
         _canIgnoreAuthAndReturnToGone.value = canWake
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index f479100..026f8f9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -124,8 +124,8 @@
     /**
      * Sends TransitionSteps between [from] and [to], calling [runCurrent] after each step.
      *
-     * By default, sends steps through FINISHED (STARTED, RUNNING, FINISHED) but can be halted part
-     * way using [throughTransitionState].
+     * By default, sends steps through FINISHED (STARTED, RUNNING @0.5f, RUNNING @1f, FINISHED) but
+     * can be halted part way using [throughTransitionState].
      */
     suspend fun sendTransitionSteps(
         from: KeyguardState,
@@ -137,6 +137,25 @@
     }
 
     /**
+     * Sends a STARTED step between [from] and [to], followed by two RUNNING steps at value
+     * [throughValue] / 2 and [throughValue], calling [runCurrent] after each step.
+     */
+    suspend fun sendTransitionStepsThroughRunning(
+        from: KeyguardState,
+        to: KeyguardState,
+        testScope: TestScope,
+        throughValue: Float = 1f,
+    ) {
+        sendTransitionSteps(
+            from,
+            to,
+            testScope.testScheduler,
+            TransitionState.RUNNING,
+            throughValue,
+        )
+    }
+
+    /**
      * Sends the provided [step] and makes sure that all previous [TransitionState]'s are sent when
      * [fillInSteps] is true. e.g. when a step FINISHED is provided, a step with STARTED and RUNNING
      * is also sent.
@@ -178,14 +197,15 @@
     /**
      * Sends TransitionSteps between [from] and [to], calling [runCurrent] after each step.
      *
-     * By default, sends steps through FINISHED (STARTED, RUNNING, FINISHED) but can be halted part
-     * way using [throughTransitionState].
+     * By default, sends steps through FINISHED (STARTED, RUNNING @0.5f, RUNNING @1f, FINISHED) but
+     * can be halted part way using [throughTransitionState].
      */
     suspend fun sendTransitionSteps(
         from: KeyguardState,
         to: KeyguardState,
         testScheduler: TestCoroutineScheduler,
         throughTransitionState: TransitionState = TransitionState.FINISHED,
+        throughTransitionValue: Float = 1f,
     ) {
         val lastStep = _transitions.replayCache.lastOrNull()
         if (lastStep != null && lastStep.transitionState != TransitionState.FINISHED) {
@@ -216,13 +236,14 @@
             throughTransitionState == TransitionState.RUNNING ||
                 throughTransitionState == TransitionState.FINISHED
         ) {
+            // Send two steps to better simulate RUNNING transitions.
             sendTransitionStep(
                 step =
                     TransitionStep(
                         transitionState = TransitionState.RUNNING,
                         from = from,
                         to = to,
-                        value = 0.5f,
+                        value = throughTransitionValue / 2f,
                     )
             )
             testScheduler.runCurrent()
@@ -233,7 +254,7 @@
                         transitionState = TransitionState.RUNNING,
                         from = from,
                         to = to,
-                        value = 1f,
+                        value = throughTransitionValue,
                     )
             )
             testScheduler.runCurrent()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
index a91ed0f..ef9bd82 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardSmartspaceViewModel
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.shade.LargeScreenHeaderHelper
 import java.util.Optional
 import org.mockito.Mockito.spy
@@ -99,6 +100,7 @@
                 blueprints = setOf(defaultKeyguardBlueprint, splitShadeBlueprint),
                 handler = fakeExecutorHandler,
                 assert = mock(),
+                log = logcatLogBuffer("blueprints"),
             )
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index b07de16..ff7a06c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
@@ -41,5 +43,7 @@
             communalSettingsInteractor = communalSettingsInteractor,
             swipeToDismissInteractor = swipeToDismissInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+            communalInteractor = communalInteractor,
+            communalSceneInteractor = communalSceneInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorKosmos.kt
index d5bdbdb..1b1fe59 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.keyguard.data.lightRevealScrimRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.power.domain.interactor.powerInteractor
 
 val Kosmos.lightRevealScrimInteractor by
@@ -30,5 +31,6 @@
             applicationCoroutineScope,
             scrimLogger,
             { powerInteractor },
+            backgroundDispatcher = testDispatcher,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractorKosmos.kt
new file mode 100644
index 0000000..8fd6f62
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractorKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.content.applicationContext
+import com.android.systemui.keyguard.data.repository.keyguardClockRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.wallpapers.data.repository.wallpaperRepository
+
+val Kosmos.wallpaperFocalAreaInteractor by
+    Kosmos.Fixture {
+        WallpaperFocalAreaInteractor(
+            applicationScope = applicationCoroutineScope,
+            context = applicationContext,
+            keyguardRepository = keyguardRepository,
+            shadeRepository = shadeRepository,
+            activeNotificationsInteractor = activeNotificationsInteractor,
+            keyguardClockRepository = keyguardClockRepository,
+            wallpaperRepository = wallpaperRepository,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/GlanceableHubBlurComponentFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/GlanceableHubBlurComponentFactoryKosmos.kt
new file mode 100644
index 0000000..3a04b7a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/GlanceableHubBlurComponentFactoryKosmos.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui
+
+import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
+import com.android.systemui.keyguard.ui.transitions.GlanceableHubBlurProvider
+import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.glanceableHubBlurComponentFactory by
+    Kosmos.Fixture {
+        object : GlanceableHubBlurComponent.Factory {
+            override fun create(
+                animation: KeyguardTransitionAnimationFlow.FlowBuilder
+            ): GlanceableHubBlurComponent {
+                return object : GlanceableHubBlurComponent {
+                    override fun getBlurProvider(): GlanceableHubBlurProvider {
+                        return GlanceableHubBlurProvider(animation, blurConfig)
+                    }
+                }
+            }
+        }
+    }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/transitions/FakeGlanceableHubTransition.kt
similarity index 64%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/transitions/FakeGlanceableHubTransition.kt
index aa262f9..41efd73 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/transitions/FakeGlanceableHubTransition.kt
@@ -14,14 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.utils
+package com.android.systemui.keyguard.ui.transitions
 
-import android.app.Instrumentation
+import kotlinx.coroutines.flow.MutableStateFlow
 
-object RecentTasksUtils {
-    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
-        instrumentation.uiAutomation.executeShellCommand(
-            "dumpsys activity service SystemUIService WMShell recents clearAll"
-        )
-    }
-}
\ No newline at end of file
+class FakeGlanceableHubTransition : GlanceableHubTransition {
+    override val windowBlurRadius: MutableStateFlow<Float> = MutableStateFlow(0.0f)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModelKosmos.kt
index 87c3dbf..ec75cfd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModelKosmos.kt
@@ -16,10 +16,14 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.keyguard.ui.glanceableHubBlurComponentFactory
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 
 val Kosmos.dozingToGlanceableHubTransitionViewModel by Fixture {
-    DozingToGlanceableHubTransitionViewModel(animationFlow = keyguardTransitionAnimationFlow)
+    DozingToGlanceableHubTransitionViewModel(
+        animationFlow = keyguardTransitionAnimationFlow,
+        blurFactory = glanceableHubBlurComponentFactory,
+    )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelKosmos.kt
index 00741eb..0dc99c8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.keyguard.ui.glanceableHubBlurComponentFactory
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 
@@ -25,5 +26,6 @@
         DreamingToGlanceableHubTransitionViewModel(
             configurationInteractor = configurationInteractor,
             animationFlow = keyguardTransitionAnimationFlow,
+            blurFactory = glanceableHubBlurComponentFactory,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDozingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDozingTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..392bbd5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDozingTransitionViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.glanceableHubBlurComponentFactory
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.glanceableHubToDozingTransitionViewModel by
+    Kosmos.Fixture {
+        GlanceableHubToDozingTransitionViewModel(
+            animationFlow = keyguardTransitionAnimationFlow,
+            blurComponentFactory = glanceableHubBlurComponentFactory,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelKosmos.kt
index 1302f15..c2c9ca7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.keyguard.ui.glanceableHubBlurComponentFactory
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 
@@ -25,5 +26,6 @@
         GlanceableHubToDreamingTransitionViewModel(
             configurationInteractor = configurationInteractor,
             animationFlow = keyguardTransitionAnimationFlow,
+            blurFactory = glanceableHubBlurComponentFactory,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt
index bb1098f..530981c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.keyguard.ui.glanceableHubBlurComponentFactory
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -25,5 +26,6 @@
     GlanceableHubToLockscreenTransitionViewModel(
         configurationInteractor = configurationInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
+        blurFactory = glanceableHubBlurComponentFactory,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..adb892d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.glanceableHubBlurComponentFactory
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.glanceableHubToOccludedTransitionViewModel by
+    Kosmos.Fixture {
+        GlanceableHubToOccludedTransitionViewModel(
+            animationFlow = keyguardTransitionAnimationFlow,
+            blurFactory = glanceableHubBlurComponentFactory,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelKosmos.kt
index c1c0807..b233d3f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.blurConfig
 import com.android.systemui.kosmos.Kosmos
@@ -25,5 +26,6 @@
     GlanceableHubToPrimaryBouncerTransitionViewModel(
         animationFlow = keyguardTransitionAnimationFlow,
         blurConfig = blurConfig,
+        communalSettingsInteractor = communalSettingsInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt
index ab7ccb3..b33d702 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
 
 val Kosmos.keyguardBlueprintViewModel by
     Kosmos.Fixture {
@@ -27,5 +28,6 @@
             fakeExecutorHandler,
             keyguardBlueprintInteractor,
             keyguardTransitionInteractor,
+            blueprintLog = logcatLogBuffer("blueprints"),
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelFactoryKosmos.kt
new file mode 100644
index 0000000..16d3fdc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelFactoryKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
+
+val Kosmos.keyguardMediaViewModelFactory by
+    Kosmos.Fixture {
+        object : KeyguardMediaViewModel.Factory {
+            override fun create(): KeyguardMediaViewModel {
+                return KeyguardMediaViewModel(mediaCarouselInteractor, keyguardInteractor)
+            }
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 40b8e0e..37df05b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor
+import com.android.systemui.keyguard.domain.interactor.wallpaperFocalAreaInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -87,5 +88,6 @@
         screenOffAnimationController = screenOffAnimationController,
         aodBurnInViewModel = aodBurnInViewModel,
         shadeInteractor = shadeInteractor,
+        wallpaperFocalAreaInteractor = wallpaperFocalAreaInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelKosmos.kt
index 0e961cca..2172d95 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.keyguard.ui.glanceableHubBlurComponentFactory
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -25,5 +26,6 @@
     LockscreenToGlanceableHubTransitionViewModel(
         configurationInteractor = configurationInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
+        blurFactory = glanceableHubBlurComponentFactory,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..38e31db
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 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.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.glanceableHubBlurComponentFactory
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.occludedToGlanceableHubTransitionViewModel by
+    Kosmos.Fixture {
+        OccludedToGlanceableHubTransitionViewModel(
+            animationFlow = keyguardTransitionAnimationFlow,
+            blurFactory = glanceableHubBlurComponentFactory,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelKosmos.kt
index 004f97d..c97c4e3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelKosmos.kt
@@ -25,5 +25,6 @@
     OccludedToPrimaryBouncerTransitionViewModel(
         animationFlow = keyguardTransitionAnimationFlow,
         blurConfig = blurConfig,
+        shadeDependentFlows = shadeDependentFlows,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt
index 4fe18fb..2dc579c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModelKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.blurConfig
 import com.android.systemui.kosmos.Kosmos
@@ -25,5 +26,6 @@
     PrimaryBouncerToGlanceableHubTransitionViewModel(
         animationFlow = keyguardTransitionAnimationFlow,
         blurConfig = blurConfig,
+        communalSettingsInteractor = communalSettingsInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelKosmos.kt
index 2256c10..ed5dd45 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelKosmos.kt
@@ -25,5 +25,6 @@
     PrimaryBouncerToOccludedTransitionViewModel(
         animationFlow = keyguardTransitionAnimationFlow,
         blurConfig = blurConfig,
+        shadeDependentFlows = shadeDependentFlows,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index a4c2cc2..b255b51 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -1,3 +1,21 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
 package com.android.systemui.kosmos
 
 import com.android.systemui.SysuiTestCase
@@ -6,9 +24,8 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.settings.brightness.ui.BrightnessWarningToast
-import com.android.systemui.util.mockito.mock
 import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.launch
@@ -46,8 +63,6 @@
     backgroundScope.coroutineContext
 }
 var Kosmos.mainCoroutineContext: CoroutineContext by Fixture { testScope.coroutineContext }
-var Kosmos.brightnessWarningToast: BrightnessWarningToast by
-    Kosmos.Fixture { mock<BrightnessWarningToast>() }
 
 /**
  * Run this test body with a [Kosmos] as receiver, and using the [testScope] currently installed in
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
index e6256a5..6c52d54 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
@@ -19,6 +19,8 @@
 import android.app.ActivityManager
 import android.media.projection.StopReason
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 
 class FakeMediaProjectionRepository : MediaProjectionRepository {
@@ -27,9 +29,18 @@
     override val mediaProjectionState: MutableStateFlow<MediaProjectionState> =
         MutableStateFlow(MediaProjectionState.NotProjecting)
 
+    private val _projectionStartedDuringCallAndActivePostCallEvent = MutableSharedFlow<Unit>()
+
+    override val projectionStartedDuringCallAndActivePostCallEvent: Flow<Unit> =
+        _projectionStartedDuringCallAndActivePostCallEvent
+
     var stopProjectingInvoked = false
 
     override suspend fun stopProjecting(@StopReason stopReason: Int) {
         stopProjectingInvoked = true
     }
+
+    suspend fun emitProjectionStartedDuringCallAndActivePostCallEvent() {
+        _projectionStartedDuringCallAndActivePostCallEvent.emit(Unit)
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/FakeMediaProjectionManager.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/FakeMediaProjectionManager.kt
index 2b6032c..9b60051 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/FakeMediaProjectionManager.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/FakeMediaProjectionManager.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.mediaprojection.taskswitcher
 
+import android.media.projection.MediaProjectionEvent
 import android.media.projection.MediaProjectionInfo
 import android.media.projection.MediaProjectionManager
 import android.os.Binder
@@ -61,14 +62,22 @@
 
     fun dispatchOnSessionSet(
         info: MediaProjectionInfo = DEFAULT_INFO,
-        session: ContentRecordingSession?
+        session: ContentRecordingSession?,
     ) {
         callbacks.forEach { it.onRecordingSessionSet(info, session) }
     }
 
+    fun dispatchEvent(
+        event: MediaProjectionEvent,
+        info: MediaProjectionInfo? = DEFAULT_INFO,
+        session: ContentRecordingSession? = null,
+    ) {
+        callbacks.forEach { it.onMediaProjectionEvent(event, info, session) }
+    }
+
     companion object {
         fun createDisplaySession(): ContentRecordingSession =
-            ContentRecordingSession.createDisplaySession(/* displayToMirror = */ 123)
+            ContentRecordingSession.createDisplaySession(/* displayToMirror= */ 123)
 
         fun createSingleTaskSession(token: IBinder = Binder()): ContentRecordingSession =
             ContentRecordingSession.createTaskSession(token)
@@ -76,10 +85,6 @@
         private const val DEFAULT_PACKAGE_NAME = "com.media.projection.test"
         private val DEFAULT_USER_HANDLE = UserHandle.getUserHandleForUid(UserHandle.myUserId())
         private val DEFAULT_INFO =
-            MediaProjectionInfo(
-                DEFAULT_PACKAGE_NAME,
-                DEFAULT_USER_HANDLE,
-                /* launchCookie = */ null
-            )
+            MediaProjectionInfo(DEFAULT_PACKAGE_NAME, DEFAULT_USER_HANDLE, /* launchCookie= */ null)
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
index 9d73ae3..a1e7d5c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
@@ -40,7 +40,8 @@
     private val callbacks = CopyOnWriteArraySet<VolumeDialogController.Callbacks>()
 
     private var hasVibrator: Boolean = true
-    private var state = VolumeDialogController.State()
+    var state = VolumeDialogController.State()
+        private set
 
     override fun setActiveStream(stream: Int) {
         updateState {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
index 01e357e..8aa98af 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
 import com.android.systemui.security.data.repository.securityRepository
 import com.android.systemui.settings.userTracker
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
 import com.android.systemui.statusbar.policy.deviceProvisionedController
 import com.android.systemui.statusbar.policy.securityController
 import com.android.systemui.user.data.repository.userSwitcherRepository
@@ -94,6 +95,7 @@
         context = applicationContext,
         falsingManager = falsingManager,
         footerActionsInteractor = footerActionsInteractor,
+        shadeModeInteractor = shadeModeInteractor,
         globalActionsDialogLiteProvider = { mock() },
         activityStarter,
         showPowerButton = true,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
index 9edeb0c..4330770 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
@@ -39,8 +39,10 @@
 import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
 import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractorImpl
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.createFooterActionsViewModel
 import com.android.systemui.security.data.repository.SecurityRepository
 import com.android.systemui.security.data.repository.SecurityRepositoryImpl
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.statusbar.policy.FakeSecurityController
 import com.android.systemui.statusbar.policy.FakeUserInfoController
@@ -56,6 +58,7 @@
 import com.android.systemui.util.settings.FakeGlobalSettings
 import com.android.systemui.util.settings.GlobalSettings
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestCoroutineScheduler
 
@@ -86,10 +89,12 @@
         falsingManager: FalsingManager = FalsingManagerFake(),
         globalActionsDialogLite: GlobalActionsDialogLite = mock(),
         showPowerButton: Boolean = true,
+        shadeMode: ShadeMode,
     ): FooterActionsViewModel {
-        return FooterActionsViewModel(
+        return createFooterActionsViewModel(
             context,
             footerActionsInteractor,
+            flowOf(shadeMode),
             falsingManager,
             globalActionsDialogLite,
             mockActivityStarter,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt
index 52d8a3a..75ca311 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.globalactions.globalActionsDialogLite
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.footerActionsInteractor
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
 
 val Kosmos.toolbarViewModelFactory by
     Kosmos.Fixture {
@@ -31,6 +32,7 @@
                     footerActionsInteractor,
                     { globalActionsDialogLite },
                     falsingInteractor,
+                    shadeModeInteractor,
                     applicationContext,
                 )
             }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt
index e46ede6..e9ba425 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.scene.sceneContainerConfig
 import com.android.systemui.scene.shared.logger.sceneLogger
 
@@ -25,5 +26,6 @@
     SceneBackInteractor(
         logger = sceneLogger,
         sceneContainerConfig = sceneContainerConfig,
+        tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index 72c7500..65bfafb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.model.sysUiState
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.domain.interactor.disabledContentInteractor
@@ -46,6 +47,7 @@
 import com.android.systemui.scene.shared.logger.sceneLogger
 import com.android.systemui.settings.displayTracker
 import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
 import com.android.systemui.statusbar.notificationShadeWindowController
 import com.android.systemui.statusbar.phone.centralSurfacesOptional
@@ -87,5 +89,7 @@
         msdlPlayer = msdlPlayer,
         disabledContentInteractor = disabledContentInteractor,
         activityTransitionAnimator = activityTransitionAnimator,
+        shadeModeInteractor = shadeModeInteractor,
+        tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
index e143324..eb494f7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
@@ -32,7 +32,7 @@
 import org.junit.Assert
 
 /** Sets up shade state for tests for either value of the scene container flag. */
-class ShadeTestUtil constructor(val delegate: ShadeTestUtilDelegate) {
+class ShadeTestUtil(val delegate: ShadeTestUtilDelegate) {
 
     /** Sets shade expansion to a value between 0-1. */
     fun setShadeExpansion(shadeExpansion: Float) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
index 4d71874..d9a348d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade.data.repository
 
+import com.android.systemui.display.data.repository.FakeFocusedDisplayRepository
 import com.android.systemui.display.data.repository.displayRepository
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
@@ -23,6 +24,7 @@
 import com.android.systemui.shade.display.AnyExternalShadeDisplayPolicy
 import com.android.systemui.shade.display.DefaultDisplayShadePolicy
 import com.android.systemui.shade.display.FakeShadeDisplayPolicy
+import com.android.systemui.shade.display.FocusShadeDisplayPolicy
 import com.android.systemui.shade.display.ShadeDisplayPolicy
 import com.android.systemui.shade.display.ShadeExpansionIntent
 import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
@@ -78,3 +80,10 @@
 
 val Kosmos.fakeShadeDisplaysRepository: FakeShadeDisplayRepository by
     Kosmos.Fixture { FakeShadeDisplayRepository() }
+val Kosmos.fakeFocusedDisplayRepository: FakeFocusedDisplayRepository by
+    Kosmos.Fixture { FakeFocusedDisplayRepository() }
+
+val Kosmos.focusShadeDisplayPolicy: FocusShadeDisplayPolicy by
+    Kosmos.Fixture {
+        FocusShadeDisplayPolicy(focusedDisplayRepository = fakeFocusedDisplayRepository)
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
index 7892e96..2ba9c80 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
@@ -16,14 +16,63 @@
 
 package com.android.systemui.shade.domain.interactor
 
+import android.content.testableContext
+import android.provider.Settings
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.table.logcatTableLogBuffer
+import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.fakeShadeRepository
 import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRepository
 
 val Kosmos.shadeModeInteractor by Fixture {
     ShadeModeInteractorImpl(
         applicationScope = applicationCoroutineScope,
         repository = shadeRepository,
+        secureSettingsRepository = fakeSecureSettingsRepository,
+        tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
     )
 }
+
+// TODO(b/391578667): Make this user-aware once supported by FakeSecureSettingsRepository.
+/**
+ * Enables the Dual Shade setting, and (optionally) sets the shade layout to be wide (`true`) or
+ * narrow (`false`).
+ *
+ * In a wide layout, notifications and quick settings shades each take up only half the screen
+ * width. In a narrow layout, they each take up the entire screen width.
+ */
+fun Kosmos.enableDualShade(wideLayout: Boolean? = null) {
+    fakeSecureSettingsRepository.setBool(Settings.Secure.DUAL_SHADE, true)
+
+    if (wideLayout != null) {
+        overrideLargeScreenResources(isLargeScreen = wideLayout)
+        fakeShadeRepository.setShadeLayoutWide(wideLayout)
+    }
+}
+
+// TODO(b/391578667): Make this user-aware once supported by FakeSecureSettingsRepository.
+fun Kosmos.disableDualShade() {
+    fakeSecureSettingsRepository.setBool(Settings.Secure.DUAL_SHADE, false)
+}
+
+fun Kosmos.enableSingleShade() {
+    disableDualShade()
+    overrideLargeScreenResources(isLargeScreen = false)
+    fakeShadeRepository.setShadeLayoutWide(false)
+}
+
+fun Kosmos.enableSplitShade() {
+    disableDualShade()
+    overrideLargeScreenResources(isLargeScreen = true)
+    fakeShadeRepository.setShadeLayoutWide(true)
+}
+
+private fun Kosmos.overrideLargeScreenResources(isLargeScreen: Boolean) {
+    with(testableContext.orCreateTestableResources) {
+        addOverride(R.bool.config_use_split_notification_shade, isLargeScreen)
+        addOverride(R.bool.config_use_large_screen_shade_header, isLargeScreen)
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java
index 6cd6594..c6daed1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -61,6 +61,7 @@
     private boolean mIsBubble = false;
     private int mProposedImportance = IMPORTANCE_UNSPECIFIED;
     private boolean mSensitiveContent = false;
+    private String mSummarization = null;
 
     public RankingBuilder() {
     }
@@ -92,6 +93,7 @@
         mIsBubble = ranking.isBubble();
         mProposedImportance = ranking.getProposedImportance();
         mSensitiveContent = ranking.hasSensitiveContent();
+        mSummarization = ranking.getSummarization();
     }
 
     public Ranking build() {
@@ -122,7 +124,8 @@
                 mRankingAdjustment,
                 mIsBubble,
                 mProposedImportance,
-                mSensitiveContent);
+                mSensitiveContent,
+                mSummarization);
         return ranking;
     }
 
@@ -262,6 +265,11 @@
         return this;
     }
 
+    public RankingBuilder setSummarization(String summary) {
+        mSummarization = summary;
+        return this;
+    }
+
     private static <E> ArrayList<E> copyList(List<E> list) {
         if (list == null) {
             return null;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelKosmos.kt
index ca33a86..f679fa4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel
 
 import android.content.applicationContext
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
@@ -31,6 +32,7 @@
             zenModeInteractor,
             seenNotificationsInteractor,
             notificationSettingsInteractor,
+            configurationInteractor,
             testDispatcher,
             dumpManager,
         )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerKosmos.kt
new file mode 100644
index 0000000..85e8fa5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerKosmos.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 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.notification.stack
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.haptics.msdl.msdlPlayer
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.row.NotificationRowLogger
+import org.mockito.kotlin.mock
+
+var Kosmos.magneticNotificationRowManager: MagneticNotificationRowManager by
+    Kosmos.Fixture { magneticNotificationRowManagerImpl }
+
+val Kosmos.magneticNotificationRowManagerImpl by
+    Kosmos.Fixture {
+        MagneticNotificationRowManagerImpl(
+            msdlPlayer,
+            NotificationTargetsHelper(),
+            NotificationRoundnessManager(dumpManager),
+            mock<NotificationRowLogger>(),
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
index f8bf3c3..1626904 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
 import com.android.systemui.statusbar.events.domain.interactor.systemStatusEventAnimationInteractor
 import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.statusBarPopupChipsViewModel
@@ -53,6 +54,7 @@
             sceneInteractor,
             sceneContainerOcclusionInteractor,
             shadeInteractor,
+            shareToAppChipViewModel,
             ongoingActivityChipsViewModel,
             statusBarPopupChipsViewModel,
             systemStatusEventAnimationInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeZenModeController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeZenModeController.java
index 75df4e6..f239aed 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeZenModeController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeZenModeController.java
@@ -15,10 +15,8 @@
 package com.android.systemui.utils.leaks;
 
 import android.app.NotificationManager;
-import android.content.ComponentName;
 import android.net.Uri;
 import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
 import android.testing.LeakCheck;
 
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -40,11 +38,6 @@
     }
 
     @Override
-    public ZenRule getManualRule() {
-        return null;
-    }
-
-    @Override
     public ZenModeConfig getConfig() {
         return null;
     }
@@ -65,27 +58,7 @@
     }
 
     @Override
-    public ComponentName getEffectsSuppressor() {
-        return null;
-    }
-
-    @Override
-    public boolean isCountdownConditionSupported() {
-        return false;
-    }
-
-    @Override
     public int getCurrentUser() {
         return 0;
     }
-
-    @Override
-    public boolean isVolumeRestricted() {
-        return false;
-    }
-
-    @Override
-    public boolean areNotificationsHiddenInShade() {
-        return false;
-    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
index 36fa82f..4ca044d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
@@ -32,10 +32,8 @@
 import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
 import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
 import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogOverscrollViewBinder
-import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderHapticsViewBinder
 import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
 import com.android.systemui.volume.dialog.sliders.ui.volumeDialogOverscrollViewBinder
-import com.android.systemui.volume.dialog.sliders.ui.volumeDialogSliderHapticsViewBinder
 import com.android.systemui.volume.dialog.sliders.ui.volumeDialogSliderViewBinder
 import com.android.systemui.volume.mediaControllerRepository
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaControllerInteractor
@@ -65,9 +63,6 @@
         override fun sliderViewBinder(): VolumeDialogSliderViewBinder =
             localKosmos.volumeDialogSliderViewBinder
 
-        override fun sliderHapticsViewBinder(): VolumeDialogSliderHapticsViewBinder =
-            localKosmos.volumeDialogSliderHapticsViewBinder
-
         override fun overscrollViewBinder(): VolumeDialogOverscrollViewBinder =
             localKosmos.volumeDialogOverscrollViewBinder
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinderKosmos.kt
deleted file mode 100644
index d6845b1..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinderKosmos.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.volume.dialog.sliders.ui
-
-import com.android.systemui.haptics.msdl.msdlPlayer
-import com.android.systemui.haptics.vibratorHelper
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.time.systemClock
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.volumeDialogSliderInputEventsViewModel
-
-val Kosmos.volumeDialogSliderHapticsViewBinder by
-    Kosmos.Fixture {
-        VolumeDialogSliderHapticsViewBinder(
-            volumeDialogSliderInputEventsViewModel,
-            vibratorHelper,
-            msdlPlayer,
-            systemClock,
-        )
-    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinderKosmos.kt
index c6db717..484a7cc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinderKosmos.kt
@@ -16,14 +16,16 @@
 
 package com.android.systemui.volume.dialog.sliders.ui
 
+import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.volumeDialogSliderInputEventsViewModel
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.volumeDialogOverscrollViewModel
 import com.android.systemui.volume.dialog.sliders.ui.viewmodel.volumeDialogSliderViewModel
 
 val Kosmos.volumeDialogSliderViewBinder by
     Kosmos.Fixture {
         VolumeDialogSliderViewBinder(
             volumeDialogSliderViewModel,
-            volumeDialogSliderInputEventsViewModel,
+            volumeDialogOverscrollViewModel,
+            sliderHapticsViewModelFactory,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModelKosmos.kt
deleted file mode 100644
index 2de0e8f..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModelKosmos.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.volume.dialog.sliders.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.volume.dialog.sliders.domain.interactor.volumeDialogSliderInputEventsInteractor
-
-val Kosmos.volumeDialogSliderInputEventsViewModel by
-    Kosmos.Fixture {
-        VolumeDialogSliderInputEventsViewModel(
-            applicationCoroutineScope,
-            volumeDialogSliderInputEventsInteractor,
-        )
-    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
index b26081c..90bbb28 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.util.time.systemClock
 import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
 import com.android.systemui.volume.dialog.shared.volumeDialogLogger
+import com.android.systemui.volume.dialog.sliders.domain.interactor.volumeDialogSliderInputEventsInteractor
 import com.android.systemui.volume.dialog.sliders.domain.interactor.volumeDialogSliderInteractor
 import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
 
@@ -29,6 +30,7 @@
         VolumeDialogSliderViewModel(
             sliderType = volumeDialogSliderType,
             interactor = volumeDialogSliderInteractor,
+            inputEventsInteractor = volumeDialogSliderInputEventsInteractor,
             visibilityInteractor = volumeDialogVisibilityInteractor,
             coroutineScope = applicationCoroutineScope,
             volumeDialogSliderIconProvider = volumeDialogSliderIconProvider,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt
index ddb9a3f..f0c0d30 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt
@@ -19,7 +19,6 @@
 import android.content.applicationContext
 import com.android.app.wallpaperManager
 import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.keyguard.data.repository.keyguardClockRepository
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -34,8 +33,7 @@
         bgDispatcher = testDispatcher,
         broadcastDispatcher = broadcastDispatcher,
         userRepository = userRepository,
-        wallpaperManager = wallpaperManager,
-        keyguardClockRepository = keyguardClockRepository,
         keyguardRepository = keyguardRepository,
+        wallpaperManager = wallpaperManager,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractorKosmos.kt
index 151f3d4..9328216 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractorKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.window.domain.interactor
 
+import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
@@ -28,6 +29,7 @@
             repository = windowRootViewBlurRepository,
             keyguardInteractor = keyguardInteractor,
             keyguardTransitionInteractor = keyguardTransitionInteractor,
+            communalInteractor = communalInteractor,
             applicationScope = applicationCoroutineScope,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt
index 864048d..5a02bfb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.window.ui.viewmodel
 
 import com.android.systemui.keyguard.ui.transitions.FakeBouncerTransition
+import com.android.systemui.keyguard.ui.transitions.FakeGlanceableHubTransition
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
 import org.mockito.internal.util.collections.Sets
@@ -26,5 +27,16 @@
         Sets.newSet(FakeBouncerTransition(), FakeBouncerTransition())
     }
 
+val Kosmos.fakeGlanceableHubTransitions by
+    Kosmos.Fixture<Set<FakeGlanceableHubTransition>> {
+        Sets.newSet(FakeGlanceableHubTransition(), FakeGlanceableHubTransition())
+    }
+
 val Kosmos.windowRootViewModel by
-    Kosmos.Fixture { WindowRootViewModel(fakeBouncerTransitions, windowRootViewBlurInteractor) }
+    Kosmos.Fixture {
+        WindowRootViewModel(
+            fakeBouncerTransitions,
+            fakeGlanceableHubTransitions,
+            windowRootViewBlurInteractor,
+        )
+    }
diff --git a/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt b/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
index 7e27b24..33287b7 100644
--- a/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
+++ b/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
@@ -1,2 +1,3 @@
 rule android.net.vcn.persistablebundleutils.** android.net.connectivity.android.net.vcn.persistablebundleutils.@1
-rule android.net.vcn.util.** android.net.connectivity.android.net.vcn.util.@1
\ No newline at end of file
+rule android.net.vcn.util.** android.net.connectivity.android.net.vcn.util.@1
+rule android.util.IndentingPrintWriter android.net.connectivity.android.util.IndentingPrintWriter
\ No newline at end of file
diff --git a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
index 81c7edf..b9dcc61 100644
--- a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
+++ b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
@@ -32,8 +32,7 @@
 // Without this annotation, this class will be treated as unused class and be removed during build
 // time.
 @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 public final class ConnectivityServiceInitializerB extends SystemService {
     private static final String TAG = ConnectivityServiceInitializerB.class.getSimpleName();
     private final VcnManagementService mVcnManagementService;
diff --git a/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java b/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
index c9a99d7..8edd63d 100644
--- a/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
+++ b/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
@@ -165,8 +165,7 @@
  * @hide
  */
 // TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 public class VcnManagementService extends IVcnManagementService.Stub {
     @NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
     @NonNull private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java b/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
index b04e25d..cedb2d1 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -79,8 +79,7 @@
  *
  * @hide
  */
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 public class TelephonySubscriptionTracker extends BroadcastReceiver {
     @NonNull private static final String TAG = TelephonySubscriptionTracker.class.getSimpleName();
     private static final boolean LOG_DBG = false; // STOPSHIP if true
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
index 97f86b1..0f8b288 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
@@ -77,8 +77,7 @@
  *
  * @hide
  */
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 public class Vcn extends Handler {
     private static final String TAG = Vcn.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
index 300b80f..da41117 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
@@ -174,8 +174,7 @@
  *
  * @hide
  */
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 public class VcnGatewayConnection extends StateMachine {
     private static final String TAG = VcnGatewayConnection.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
index 38fcf09..bc815eb 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
@@ -58,8 +58,7 @@
  */
 // TODO(b/388919146): Implement a more generic solution to prevent concurrent modifications on
 // mListeners and mRequests
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 public class VcnNetworkProvider extends NetworkProvider {
     private static final String TAG = VcnNetworkProvider.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index c8c645f..aff7068 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -61,8 +61,7 @@
  *
  * <p>This class is flag gated by "network_metric_monitor" and "ipsec_tramsform_state"
  */
-// TODO(b/374174952) Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 public class IpSecPacketLossDetector extends NetworkMetricMonitor {
     private static final String TAG = IpSecPacketLossDetector.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index 55829a5..fc9c7ac 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -44,8 +44,7 @@
  *
  * <p>This class is flag gated by "network_metric_monitor"
  */
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 public abstract class NetworkMetricMonitor implements AutoCloseable {
     private static final String TAG = NetworkMetricMonitor.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 705141f..7cb3257 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -52,8 +52,7 @@
 import java.util.Set;
 
 /** @hide */
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 class NetworkPriorityClassifier {
     @NonNull private static final String TAG = NetworkPriorityClassifier.class.getSimpleName();
     /**
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index bc552e7..37ec0e8 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -75,8 +75,7 @@
  *
  * @hide
  */
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 public class UnderlyingNetworkController {
     @NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName();
 
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
index 776931b..164b59f 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -52,8 +52,7 @@
  *
  * @hide
  */
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
 public class UnderlyingNetworkEvaluator {
     private static final String TAG = UnderlyingNetworkEvaluator.class.getSimpleName();
 
diff --git a/packages/VpnDialogs/res/values-in/strings.xml b/packages/VpnDialogs/res/values-in/strings.xml
index c67e5db..ae01f21 100644
--- a/packages/VpnDialogs/res/values-in/strings.xml
+++ b/packages/VpnDialogs/res/values-in/strings.xml
@@ -30,7 +30,7 @@
     <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> disiapkan untuk selalu terhubung, tetapi saat ini tidak dapat terhubung. Anda akan terhubung jika VPN dapat terhubung ulang."</string>
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Ubah setelan VPN"</string>
-    <string name="configure" msgid="4905518375574791375">"Konfigurasikan"</string>
+    <string name="configure" msgid="4905518375574791375">"Konfigurasi"</string>
     <string name="disconnect" msgid="971412338304200056">"Berhenti hubungkan"</string>
     <string name="open_app" msgid="3717639178595958667">"Buka aplikasi"</string>
     <string name="dismiss" msgid="6192859333764711227">"Tutup"</string>
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 8e99842..65550f2 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -175,9 +175,9 @@
 }
 
 java_device_for_host {
-    name: "ravenwood-junit-impl-for-ravenizer",
+    name: "ravenwood-junit-for-ravenizer",
     libs: [
-        "ravenwood-junit-impl",
+        "ravenwood-junit",
     ],
     visibility: [":__subpackages__"],
 }
@@ -661,6 +661,9 @@
         // StatsD
         "framework-statsd.ravenwood",
 
+        // Graphics
+        "framework-graphics.ravenwood",
+
         // Provide runtime versions of utils linked in below
         "junit",
         "truth",
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index 99fc31b..71496b0 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -399,3 +399,55 @@
         "framework-statsd.ravenwood.jar",
     ],
 }
+
+//////////////////////
+// framework-graphics
+//////////////////////
+
+java_genrule {
+    name: "framework-graphics.ravenwood-base",
+    tools: ["hoststubgen"],
+    cmd: "$(location hoststubgen) " +
+        "@$(location :ravenwood-standard-options) " +
+
+        "--debug-log $(location framework-graphics.log) " +
+        "--stats-file $(location framework-graphics_stats.csv) " +
+        "--supported-api-list-file $(location framework-graphics_apis.csv) " +
+        "--gen-keep-all-file $(location framework-graphics_keep_all.txt) " +
+        "--gen-input-dump-file $(location framework-graphics_dump.txt) " +
+
+        "--out-impl-jar $(location ravenwood.jar) " +
+        "--in-jar $(location :framework-graphics.impl{.jar}) " +
+
+        "--policy-override-file $(location :ravenwood-common-policies) ",
+    srcs: [
+        ":framework-graphics.impl{.jar}",
+
+        ":ravenwood-common-policies",
+        ":ravenwood-standard-options",
+    ],
+    out: [
+        "ravenwood.jar",
+
+        // Following files are created just as FYI.
+        "framework-graphics_keep_all.txt",
+        "framework-graphics_dump.txt",
+
+        "framework-graphics.log",
+        "framework-graphics_stats.csv",
+        "framework-graphics_apis.csv",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+java_genrule {
+    name: "framework-graphics.ravenwood",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
+    cmd: "cp $(in) $(out)",
+    srcs: [
+        ":framework-graphics.ravenwood-base{ravenwood.jar}",
+    ],
+    out: [
+        "framework-graphics.ravenwood.jar",
+    ],
+}
diff --git a/ravenwood/scripts/add-annotations.sh b/ravenwood/scripts/add-annotations.sh
new file mode 100755
index 0000000..3e86037
--- /dev/null
+++ b/ravenwood/scripts/add-annotations.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+# Copyright (C) 2025 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.
+
+#
+# Use "ravehleper mm" to create a shell script which:
+# - Reads read a list of methods from STDIN
+#   Which basically looks like a list of 'com.android.ravenwoodtest.tests.Test1#testA'
+# - Add @DisabledOnRavenwood to them
+#
+# Example usage:
+#
+# ./add-annotations.sh $ANDROID_BUILD_TOP/frameworks/base/ravenwood/tests <METHOD-LIST.txt
+#
+# Use a different annotation instead. (Note, in order to use an at, you need to use a double-at.)
+# ./add-annotations.sh -t '@@Ignore' $ANDROID_BUILD_TOP/frameworks/base/ravenwood/tests <METHOD-LIST.txt
+#
+
+set -e
+
+# Uncomment it to always build ravenhelper (slow)
+# ${BUILD_CMD:-m} ravenhelper
+
+# We add this line to each methods found.
+# Note, if we used a single @, that'd be handled as an at file. Use
+# the double-at instead.
+annotation="@@android.platform.test.annotations.DisabledOnRavenwood"
+while getopts "t:" opt; do
+case "$opt" in
+    t)
+        annotation="$OPTARG"
+        ;;
+    '?')
+        exit 1
+        ;;
+esac
+done
+shift $(($OPTIND - 1))
+
+source_dirs="$@"
+
+OUT_SCRIPT="${OUT_SCRIPT:-/tmp/add-annotations.sh}"
+
+rm -f "$OUT_SCRIPT"
+
+
+with_flag() {
+    local flag="$1"
+    shift
+
+    for arg in "$@"; do
+        echo "$flag $arg"
+    done
+}
+
+run() {
+    echo "Running: $*"
+    "$@"
+}
+
+run ${RAVENHELPER_CMD:-ravenhelper mm} \
+    --output-script $OUT_SCRIPT \
+    --text "$annotation" \
+    $(with_flag --src $source_dirs)
+
+
+if ! [[ -f $OUT_SCRIPT ]] ; then
+    # no operations generated.
+    exit 0
+fi
+
+echo
+echo "Created script at $OUT_SCRIPT. Run it with: sh $OUT_SCRIPT"
diff --git a/ravenwood/scripts/pta-framework.sh b/ravenwood/scripts/pta-framework.sh
index 224ab59..d396839 100755
--- a/ravenwood/scripts/pta-framework.sh
+++ b/ravenwood/scripts/pta-framework.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright (C) 2024 The Android Open Source Project
+# Copyright (C) 2025 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.
@@ -79,6 +79,7 @@
         $extra_args
 
     if ! [[ -f $OUT_SCRIPT ]] ; then
+        echo "No files need updating."
         # no operations generated.
         exit 0
     fi
@@ -88,4 +89,4 @@
     return 0
 }
 
-run_pta "$extra_args"
\ No newline at end of file
+run_pta "$extra_args"
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 4033782..fff9e6a 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -62,4 +62,4 @@
 
 # Just enough to allow ResourcesManager to run
 class android.hardware.display.DisplayManagerGlobal keep # no-pta
-    method getInstance ()Landroid/hardware/display/DisplayManagerGlobal; ignore
+    method getInstance ()Landroid/hardware/display/DisplayManagerGlobal; ignore # no-pta
diff --git a/ravenwood/texts/ravenwood-standard-options.txt b/ravenwood/texts/ravenwood-standard-options.txt
index 27223d8b..91fd928 100644
--- a/ravenwood/texts/ravenwood-standard-options.txt
+++ b/ravenwood/texts/ravenwood-standard-options.txt
@@ -31,6 +31,9 @@
 --remove-annotation
     android.ravenwood.annotation.RavenwoodRemove
 
+--ignore-annotation
+    android.ravenwood.annotation.RavenwoodIgnore
+
 --substitute-annotation
     android.ravenwood.annotation.RavenwoodReplace
 
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index ae9276f..297420d 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -354,6 +354,8 @@
  * Scan the arguments, and if any of them starts with an `@`, then load from the file
  * and use its content as arguments.
  *
+ * In order to pass an argument that starts with an '@', use '@@' instead.
+ *
  * In this file, each line is treated as a single argument.
  *
  * The file can contain '#' as comments.
@@ -362,7 +364,10 @@
     val ret = mutableListOf<String>()
 
     args.forEach { arg ->
-        if (!arg.startsWith('@')) {
+        if (arg.startsWith("@@")) {
+            ret += arg.substring(1)
+            return@forEach
+        } else if (!arg.startsWith('@')) {
             ret += arg
             return@forEach
         }
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/RavenHelperMain.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/RavenHelperMain.kt
index e6efbf6..0be0c96 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/RavenHelperMain.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/RavenHelperMain.kt
@@ -27,6 +27,7 @@
 import com.android.hoststubgen.log
 import com.android.hoststubgen.runMainWithBoilerplate
 import com.android.platform.test.ravenwood.ravenhelper.policytoannot.PtaProcessor
+import com.android.platform.test.ravenwood.ravenhelper.sourcemap.MarkMethodHandler
 
 interface SubcommandHandler {
     fun handle(args: List<String>)
@@ -39,7 +40,10 @@
 
         Subcommands:
           pta:        "policy-to-annotations" Convert policy file to annotations.
-                      (See the pta-framework.sh script for usage.) 1
+                      (See the pta-framework.sh script for usage.)
+
+          mm:         "mark methods" Used to add annotations (such as @DisabledOnRavenwood)
+                      to methods.
 
         """.trimIndent())
 }
@@ -60,6 +64,7 @@
         val subcommand = args[0]
         val handler: SubcommandHandler = when (subcommand) {
             "pta" -> PtaProcessor()
+            "mm" -> MarkMethodHandler()
             else -> {
                 usage()
                 throw GeneralUserErrorException("Unknown subcommand '$subcommand'")
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt
index 4a11259..ef1cb5d 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt
@@ -45,7 +45,8 @@
                 "@android.ravenwood.annotation.RavenwoodRedirect"
             FilterPolicy.Throw ->
                 "@android.ravenwood.annotation.RavenwoodThrow"
-            FilterPolicy.Ignore -> null // Ignore has no annotation. (because it's not very safe.)
+            FilterPolicy.Ignore ->
+                "@android.ravenwood.annotation.RavenwoodIgnore"
             FilterPolicy.Remove ->
                 "@android.ravenwood.annotation.RavenwoodRemove"
         }
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Operations.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Operations.kt
index 3531ba95..256d123 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Operations.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Operations.kt
@@ -24,6 +24,8 @@
 import com.android.hoststubgen.log
 import java.io.BufferedWriter
 import java.io.File
+import java.io.FileOutputStream
+import java.io.OutputStreamWriter
 
 enum class SourceOperationType {
     /** Insert a line */
@@ -198,4 +200,26 @@
             }
         }
     }
+}
+
+fun createShellScript(ops: SourceOperations, scriptFile: String?): Boolean {
+    if (ops.size == 0) {
+        log.i("No files need to be updated.")
+        return false
+    }
+
+    val scriptWriter = BufferedWriter(
+        OutputStreamWriter(
+            scriptFile?.let { file ->
+            FileOutputStream(file)
+        } ?: System.out
+    ))
+
+    scriptWriter.use { writer ->
+        scriptFile?.let {
+            log.i("Creating script file at $it ...")
+        }
+        createShellScript(ops, writer)
+    }
+    return true
 }
\ No newline at end of file
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
index 5984e4f..3657a90 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
@@ -31,10 +31,7 @@
 import com.android.platform.test.ravenwood.ravenhelper.sourcemap.ClassInfo
 import com.android.platform.test.ravenwood.ravenhelper.sourcemap.MethodInfo
 import com.android.platform.test.ravenwood.ravenhelper.sourcemap.SourceLoader
-import java.io.BufferedWriter
-import java.io.FileOutputStream
 import java.io.FileReader
-import java.io.OutputStreamWriter
 import java.util.regex.Pattern
 
 /**
@@ -55,25 +52,7 @@
         )
         converter.process()
 
-        val ops = converter.resultOperations
-
-        if (ops.size == 0) {
-            log.i("No files need to be updated.")
-            return
-        }
-
-        val scriptWriter = BufferedWriter(OutputStreamWriter(
-            options.outputScriptFile.get?.let { file ->
-                FileOutputStream(file)
-            } ?: System.out
-        ))
-
-        scriptWriter.use { writer ->
-            options.outputScriptFile.get?.let {
-                log.i("Creating script file at $it ...")
-            }
-            createShellScript(ops, writer)
-        }
+        createShellScript(converter.resultOperations, options.outputScriptFile.get)
     }
 }
 
@@ -424,7 +403,7 @@
 
             if (methodsAndAnnot == null) {
                 classHasMember = true
-                return // This policy can't converted.
+                return // This policy can't be converted.
             }
             val methods = methodsAndAnnot.first
             val annot = methodsAndAnnot.second
@@ -476,4 +455,4 @@
             classHasMember = true
         }
     }
-}
\ No newline at end of file
+}
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MapOptions.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MapOptions.kt
new file mode 100644
index 0000000..ee200bb
--- /dev/null
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MapOptions.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2025 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.platform.test.ravenwood.ravenhelper.sourcemap
+
+import com.android.hoststubgen.ArgIterator
+import com.android.hoststubgen.ArgumentsException
+import com.android.hoststubgen.SetOnce
+import com.android.hoststubgen.ensureFileExists
+import com.android.hoststubgen.log
+
+/**
+ * Options for the "ravenhelper map" subcommand.
+ */
+class MapOptions(
+    /** Source files or directories. */
+    var sourceFilesOrDirectories: MutableList<String> = mutableListOf(),
+
+    /** Files containing target methods */
+    var targetMethodFiles: MutableList<String> = mutableListOf(),
+
+    /** Output script file. */
+    var outputScriptFile: SetOnce<String?> = SetOnce(null),
+
+    /** Text to insert. */
+    var text: SetOnce<String?> = SetOnce(null),
+) {
+    companion object {
+        fun parseArgs(args: List<String>): MapOptions {
+            val ret = MapOptions()
+            val ai = ArgIterator.withAtFiles(args.toTypedArray())
+
+            while (true) {
+                val arg = ai.nextArgOptional() ?: break
+
+                fun nextArg(): String = ai.nextArgRequired(arg)
+
+                if (log.maybeHandleCommandLineArg(arg) { nextArg() }) {
+                    continue
+                }
+                try {
+                    when (arg) {
+                        // TODO: Write help
+                        "-h", "--help" -> TODO("Help is not implemented yet")
+
+                        "-s", "--src" ->
+                            ret.sourceFilesOrDirectories.add(nextArg().ensureFileExists())
+
+                        "-i", "--input" ->
+                            ret.targetMethodFiles.add(nextArg().ensureFileExists())
+
+                        "-o", "--output-script" ->
+                            ret.outputScriptFile.set(nextArg())
+
+                        "-t", "--text" ->
+                            ret.text.set(nextArg())
+
+                        else -> throw ArgumentsException("Unknown option: $arg")
+                    }
+                } catch (e: SetOnce.SetMoreThanOnceException) {
+                    throw ArgumentsException("Duplicate or conflicting argument found: $arg")
+                }
+            }
+
+            if (ret.sourceFilesOrDirectories.size == 0) {
+                throw ArgumentsException("Must specify at least one source path")
+            }
+
+            return ret
+        }
+    }
+
+    override fun toString(): String {
+        return """
+            PtaOptions{
+              sourceFilesOrDirectories=$sourceFilesOrDirectories
+              targetMethods=$targetMethodFiles
+              outputScriptFile=$outputScriptFile
+              text=$text
+            }
+            """.trimIndent()
+    }
+}
\ No newline at end of file
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MarkMethodHandler.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MarkMethodHandler.kt
new file mode 100644
index 0000000..8085253
--- /dev/null
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MarkMethodHandler.kt
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2025 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.platform.test.ravenwood.ravenhelper.sourcemap
+
+import com.android.hoststubgen.GeneralUserErrorException
+import com.android.hoststubgen.log
+import com.android.platform.test.ravenwood.ravenhelper.SubcommandHandler
+import com.android.platform.test.ravenwood.ravenhelper.policytoannot.SourceOperation
+import com.android.platform.test.ravenwood.ravenhelper.policytoannot.SourceOperationType
+import com.android.platform.test.ravenwood.ravenhelper.policytoannot.SourceOperations
+import com.android.platform.test.ravenwood.ravenhelper.policytoannot.createShellScript
+import com.android.platform.test.ravenwood.ravenhelper.psi.createUastEnvironment
+import java.io.BufferedReader
+import java.io.FileReader
+
+/**
+ * This is the main routine of the "mm" subcommands, which marks specified methods with
+ * a given line, which defaults to "@DisabledOnRavenwood". This can be used to add bulk-annotate
+ * tests methods that are failing.
+ *
+ * See the javadoc of [MarkMethodProcessor] for more details.
+ */
+class MarkMethodHandler : SubcommandHandler {
+    override fun handle(args: List<String>) {
+        val options = MapOptions.parseArgs(args)
+
+        log.i("Options: $options")
+
+        // Files from which we load a list of methods.
+        val inputFiles = if (options.targetMethodFiles.isEmpty()) {
+            log.w("[Reading method list from STDIN...]")
+            log.flush()
+            listOf("/dev/stdin")
+        } else {
+            options.targetMethodFiles
+        }
+
+        // A string to inject before each method.
+        val text = if (options.text.isSet) {
+            options.text.get!!
+        } else {
+            "@android.platform.test.annotations.DisabledOnRavenwood"
+        }
+
+        // Process.
+        val processor = MarkMethodProcessor(
+            options.sourceFilesOrDirectories,
+            inputFiles,
+            text,
+        )
+        processor.process()
+
+        // Create the output script.
+        createShellScript(processor.resultOperations, options.outputScriptFile.get)
+    }
+}
+
+/**
+ * Load a list of methods / classes from [targetMethodFiles], and inject [textToInsert] to
+ * each of them, to the source files under [sourceFilesOrDirectories]
+ *
+ * An example input files look like this -- this can be generated from atest output.
+ * <pre>
+
+ # We add @DisabledOnRavenwood to the following methods.
+ com.android.ravenwoodtest.tests.Test1#testA
+ com.android.ravenwoodtest.tests.Test1#testB
+ com.android.ravenwoodtest.tests.Test1#testC
+
+ # We add @DisabledOnRavenwood to the following class.
+ com.android.ravenwoodtest.tests.Test2
+
+ # Special case: we add the annotation to the class too.
+ com.android.ravenwoodtest.tests.Test3#initializationError
+ </pre>
+
+ */
+private class MarkMethodProcessor(
+    private val sourceFilesOrDirectories: List<String>,
+    private val targetMethodFiles: List<String>,
+    private val textToInsert: String,
+) {
+    private val classes = AllClassInfo()
+    val resultOperations = SourceOperations()
+
+    /**
+     * Entry point.
+     */
+    fun process() {
+        val env = createUastEnvironment()
+        try {
+            loadSources()
+
+            processInputFiles()
+        } finally {
+            env.dispose()
+        }
+    }
+
+    private fun loadSources() {
+        val env = createUastEnvironment()
+        try {
+            val loader = SourceLoader(env)
+            loader.load(sourceFilesOrDirectories, classes)
+        } finally {
+            env.dispose()
+        }
+    }
+
+    /**
+     * Process liput files. Input files looks like this:
+     * <pre>
+     * # We add @DisabledOnRavenwood to the following methods.
+     * com.android.ravenwoodtest.tests.Test1#testA
+     * com.android.ravenwoodtest.tests.Test1#testB
+     * com.android.ravenwoodtest.tests.Test1#testC
+     *
+     * # We add @DisabledOnRavenwood to the following class.
+     * com.android.ravenwoodtest.tests.Test2
+     *
+     * # Special case: we add the annotation to the class too.
+     * com.android.ravenwoodtest.tests.Test3#initializationError
+     * </pre>
+     */
+    private fun processInputFiles() {
+        targetMethodFiles.forEach { filename ->
+            BufferedReader(FileReader(filename)).use { reader ->
+                reader.readLines().forEach { line ->
+                    if (line.isBlank() || line.startsWith('#')) {
+                        return@forEach
+                    }
+                    processSingleLine(line)
+                }
+            }
+        }
+    }
+
+    private fun processSingleLine(line: String) {
+        val cm = line.split("#") // Class and method
+        if (cm.size > 2) {
+            throw GeneralUserErrorException("Input line \"$line\" contains too many #'s")
+        }
+        val className = cm[0]
+        val methodName = if (cm.size == 2 && cm[1] != "initializationError") {
+            cm[1]
+        } else {
+            ""
+        }
+
+        // Find class info
+        val ci = classes.findClass(className)
+            ?: throw GeneralUserErrorException("Class \"$className\" not found\"")
+
+        if (methodName == "") {
+            processClass(ci)
+        } else {
+            processMethod(ci, methodName)
+        }
+    }
+
+    private fun processClass(ci: ClassInfo) {
+        addOperation(ci.location, "Class ${ci.fullName}")
+    }
+
+    private fun processMethod(ci: ClassInfo, methodName: String) {
+        var methods = ci.methods[methodName]
+            ?: throw GeneralUserErrorException("method \"$methodName\" not found\"")
+        methods.forEach { mi ->
+            addOperation(mi.location, "Method ${mi.name}")
+        }
+    }
+
+    private fun addOperation(loc: Location, description: String) {
+        resultOperations.add(
+            SourceOperation(
+                loc.file,
+                loc.line,
+                SourceOperationType.Insert,
+                loc.getIndent() + textToInsert,
+                description
+            )
+        )
+    }
+}
diff --git a/ravenwood/tools/ravenizer/Android.bp b/ravenwood/tools/ravenizer/Android.bp
index 2892d07..a52a04b 100644
--- a/ravenwood/tools/ravenizer/Android.bp
+++ b/ravenwood/tools/ravenizer/Android.bp
@@ -19,7 +19,7 @@
         "ow2-asm-tree",
         "ow2-asm-util",
         "junit",
-        "ravenwood-junit-impl-for-ravenizer",
+        "ravenwood-junit-for-ravenizer",
     ],
     visibility: ["//visibility:public"],
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 75c629b..e422fef 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -31,6 +31,7 @@
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.provider.Settings;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -45,6 +46,7 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.server.LocalServices;
+import com.android.server.accessibility.autoclick.AutoclickController;
 import com.android.server.accessibility.gestures.TouchExplorer;
 import com.android.server.accessibility.magnification.FullScreenMagnificationController;
 import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
@@ -70,9 +72,9 @@
  */
 class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation {
 
-    private static final String TAG = AccessibilityInputFilter.class.getSimpleName();
+    private static final String TAG = "A11yInputFilter";
 
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     /**
      * Flag for enabling the screen magnification feature.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 875b655..67fdca4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -36,6 +36,7 @@
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER;
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
 import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.hardware.input.InputSettings.isRepeatKeysFeatureFlagEnabled;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
@@ -156,6 +157,7 @@
 import android.view.MagnificationSpec;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
+import android.view.ViewConfiguration;
 import android.view.WindowInfo;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
@@ -607,7 +609,8 @@
                 mLock,
                 mContext,
                 new MagnificationScaleProvider(mContext),
-                Executors.newSingleThreadExecutor()
+                Executors.newSingleThreadExecutor(),
+                mContext.getMainLooper()
         );
         mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
         mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
@@ -3493,6 +3496,7 @@
         somethingChanged |= readMagnificationFollowTypingLocked(userState);
         somethingChanged |= readAlwaysOnMagnificationLocked(userState);
         somethingChanged |= readMouseKeysEnabledLocked(userState);
+        somethingChanged |= readRepeatKeysSettingsLocked(userState);
         return somethingChanged;
     }
 
@@ -5084,39 +5088,7 @@
             final List<String> permittedServices = dpm.getPermittedAccessibilityServices(userId);
 
             // permittedServices null means all accessibility services are allowed.
-            boolean allowed = permittedServices == null || permittedServices.contains(packageName);
-            if (allowed) {
-                if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
-                        && android.security.Flags.extendEcmToAllSettings()) {
-                    try {
-                        final EnhancedConfirmationManager userContextEcm =
-                                mContext.createContextAsUser(UserHandle.of(userId), /* flags = */ 0)
-                                        .getSystemService(EnhancedConfirmationManager.class);
-                        if (userContextEcm != null) {
-                            return !userContextEcm.isRestricted(packageName,
-                                    AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
-                        }
-                        return false;
-                    } catch (PackageManager.NameNotFoundException e) {
-                        Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
-                        return false;
-                    }
-                } else {
-                    try {
-                        final int mode = mContext.getSystemService(AppOpsManager.class)
-                                .noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
-                                        uid, packageName);
-                        final boolean ecmEnabled = mContext.getResources().getBoolean(
-                                com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
-                        return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED
-                                || mode == AppOpsManager.MODE_DEFAULT;
-                    } catch (Exception e) {
-                        // Fallback in case if app ops is not available in testing.
-                        return false;
-                    }
-                }
-            }
-            return false;
+            return permittedServices == null || permittedServices.contains(packageName);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -5802,6 +5774,12 @@
         private final Uri mUserSetupCompleteUri = Settings.Secure.getUriFor(
                 Settings.Secure.USER_SETUP_COMPLETE);
 
+        private final Uri mRepeatKeysEnabledUri = Settings.Secure.getUriFor(
+                Settings.Secure.KEY_REPEAT_ENABLED);
+
+        private final Uri mRepeatKeysTimeoutMsUri = Settings.Secure.getUriFor(
+                Settings.Secure.KEY_REPEAT_TIMEOUT_MS);
+
         public AccessibilityContentObserver(Handler handler) {
             super(handler);
         }
@@ -5858,6 +5836,12 @@
                     mNavigationModeUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mUserSetupCompleteUri, false, this, UserHandle.USER_ALL);
+            if (isRepeatKeysFeatureFlagEnabled() && Flags.enableMagnificationKeyboardControl()) {
+                contentResolver.registerContentObserver(
+                        mRepeatKeysEnabledUri, false, this, UserHandle.USER_ALL);
+                contentResolver.registerContentObserver(
+                        mRepeatKeysTimeoutMsUri, false, this, UserHandle.USER_ALL);
+            }
         }
 
         @Override
@@ -5948,6 +5932,9 @@
                     }
                 } else if (mNavigationModeUri.equals(uri) || mUserSetupCompleteUri.equals(uri)) {
                     updateShortcutsForCurrentNavigationMode();
+                } else if (mRepeatKeysEnabledUri.equals(uri)
+                        || mRepeatKeysTimeoutMsUri.equals(uri)) {
+                    readRepeatKeysSettingsLocked(userState);
                 }
             }
         }
@@ -6086,6 +6073,24 @@
         return false;
     }
 
+    boolean readRepeatKeysSettingsLocked(AccessibilityUserState userState) {
+        if (!isRepeatKeysFeatureFlagEnabled() || !Flags.enableMagnificationKeyboardControl()) {
+            return false;
+        }
+        final boolean isRepeatKeysEnabled = Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.KEY_REPEAT_ENABLED,
+                1, userState.mUserId) == 1;
+        final int repeatKeysTimeoutMs = Settings.Secure.getIntForUser(
+                mContext.getContentResolver(), Settings.Secure.KEY_REPEAT_TIMEOUT_MS,
+                ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT, userState.mUserId);
+        mMagnificationController.setRepeatKeysEnabled(isRepeatKeysEnabled);
+        mMagnificationController.setRepeatKeysTimeoutMs(repeatKeysTimeoutMs);
+
+        // No need to update any other state, so always return false.
+        return false;
+    }
+
     boolean readMouseKeysEnabledLocked(AccessibilityUserState userState) {
         if (!keyboardA11yMouseKeys()) {
             return false;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index a3fe9ec..6cba363 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -554,7 +554,8 @@
                     if (motionEventInjector != null
                             && mWindowManagerService.isTouchOrFaketouchDevice()) {
                         motionEventInjector.injectEvents(
-                                gestureSteps.getList(), mClient, sequence, displayId);
+                                gestureSteps.getList(), mClient, sequence, displayId,
+                                mAccessibilityServiceInfo.isAccessibilityTool());
                     } else {
                         try {
                             if (svcClientTracingEnabled()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
index 5cbd1a2..b216953 100644
--- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
+++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
@@ -105,12 +105,14 @@
      * either complete or cancelled.
      */
     public void injectEvents(List<GestureStep> gestureSteps,
-            IAccessibilityServiceClient serviceInterface, int sequence, int displayId) {
+            IAccessibilityServiceClient serviceInterface, int sequence, int displayId,
+            boolean fromAccessibilityTool) {
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = gestureSteps;
         args.arg2 = serviceInterface;
         args.argi1 = sequence;
         args.argi2 = displayId;
+        args.argi3 = fromAccessibilityTool ? 1 : 0;
         mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_INJECT_EVENTS, args));
     }
 
@@ -132,9 +134,11 @@
             return;
         }
         cancelAnyPendingInjectedEvents();
-        // Indicate that the input event is injected from accessibility, to let applications
-        // distinguish it from events injected by other means.
-        policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
+        if (!android.view.accessibility.Flags.preventA11yNontoolFromInjectingIntoSensitiveViews()) {
+            // Indicate that the input event is injected from accessibility, to let applications
+            // distinguish it from events injected by other means.
+            policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
+        }
         sendMotionEventToNext(event, rawEvent, policyFlags);
     }
 
@@ -159,8 +163,12 @@
     public boolean handleMessage(Message message) {
         if (message.what == MESSAGE_INJECT_EVENTS) {
             SomeArgs args = (SomeArgs) message.obj;
-            injectEventsMainThread((List<GestureStep>) args.arg1,
-                    (IAccessibilityServiceClient) args.arg2, args.argi1, args.argi2);
+            injectEventsMainThread(
+                    /*gestureSteps=*/(List<GestureStep>) args.arg1,
+                    /*serviceInterface=*/(IAccessibilityServiceClient) args.arg2,
+                    /*sequence=*/args.argi1,
+                    /*displayId=*/args.argi2,
+                    /*fromAccessibilityTool=*/args.argi3 == 1);
             args.recycle();
             return true;
         }
@@ -169,9 +177,15 @@
             return false;
         }
         MotionEvent motionEvent = (MotionEvent) message.obj;
-        sendMotionEventToNext(motionEvent, motionEvent,
-                WindowManagerPolicyConstants.FLAG_PASS_TO_USER
-                | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY);
+        int policyFlags = WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+                | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
+        if (android.view.accessibility.Flags.preventA11yNontoolFromInjectingIntoSensitiveViews()) {
+            boolean fromAccessibilityTool = message.arg2 == 1;
+            if (fromAccessibilityTool) {
+                policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL;
+            }
+        }
+        sendMotionEventToNext(motionEvent, motionEvent, policyFlags);
         boolean isEndOfSequence = message.arg1 != 0;
         if (isEndOfSequence) {
             notifyService(mServiceInterfaceForCurrentGesture, mSequencesInProgress.get(0), true);
@@ -181,7 +195,8 @@
     }
 
     private void injectEventsMainThread(List<GestureStep> gestureSteps,
-            IAccessibilityServiceClient serviceInterface, int sequence, int displayId) {
+            IAccessibilityServiceClient serviceInterface, int sequence, int displayId,
+            boolean fromAccessibilityTool) {
         if (mIsDestroyed) {
             try {
                 serviceInterface.onPerformGestureResult(sequence, false);
@@ -228,7 +243,8 @@
             event.setDisplayId(displayId);
             int isEndOfSequence = (i == events.size() - 1) ? 1 : 0;
             Message message = mHandler.obtainMessage(
-                    MESSAGE_SEND_MOTION_EVENT, isEndOfSequence, 0, event);
+                    MESSAGE_SEND_MOTION_EVENT, isEndOfSequence,
+                    fromAccessibilityTool ? 1 : 0, event);
             mLastScheduledEventTime = event.getEventTime();
             mHandler.sendMessageDelayed(message, Math.max(0, event.getEventTime() - currentTime));
         }
@@ -322,9 +338,16 @@
             long now = SystemClock.uptimeMillis();
             MotionEvent cancelEvent =
                     obtainMotionEvent(now, now, MotionEvent.ACTION_CANCEL, getLastTouchPoints(), 1);
-            sendMotionEventToNext(cancelEvent, cancelEvent,
-                    WindowManagerPolicyConstants.FLAG_PASS_TO_USER
-                    | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY);
+            int policyFlags = WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+                    | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
+            if (android.view.accessibility.Flags
+                    .preventA11yNontoolFromInjectingIntoSensitiveViews()) {
+                // ACTION_CANCEL events are internal system details for event stream state
+                // management and not used for performing new actions, so always treat them as
+                // originating from an accessibility tool.
+                policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL;
+            }
+            sendMotionEventToNext(cancelEvent, cancelEvent, policyFlags);
             mOpenGesturesInProgress.put(source, false);
         }
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
similarity index 79%
rename from services/accessibility/java/com/android/server/accessibility/AutoclickController.java
rename to services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 8b758d2..1bc9c78 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.server.accessibility;
+package com.android.server.accessibility.autoclick;
 
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.MotionEvent.BUTTON_PRIMARY;
+import static android.view.accessibility.AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
+import static android.view.accessibility.AccessibilityManager.AUTOCLICK_DELAY_DEFAULT;
+import static android.view.accessibility.AccessibilityManager.AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT;
 
-import static com.android.server.accessibility.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
+import static com.android.server.accessibility.autoclick.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
 
 import android.accessibilityservice.AccessibilityTrace;
 import android.annotation.NonNull;
@@ -26,7 +29,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
-import android.graphics.PixelFormat;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -37,10 +39,14 @@
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
 import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.accessibility.util.AccessibilityUtils;
+import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.accessibility.BaseEventStreamTransformation;
+import com.android.server.accessibility.Flags;
+
 /**
  * Implements "Automatically click on mouse stop" feature.
  *
@@ -96,8 +102,7 @@
                     initiateAutoclickIndicator(handler);
                 }
 
-                mClickScheduler =
-                        new ClickScheduler(handler, AccessibilityManager.AUTOCLICK_DELAY_DEFAULT);
+                mClickScheduler = new ClickScheduler(handler, AUTOCLICK_DELAY_DEFAULT);
                 mAutoclickSettingsObserver = new AutoclickSettingsObserver(mUserId, handler);
                 mAutoclickSettingsObserver.start(
                         mContext.getContentResolver(),
@@ -117,21 +122,8 @@
         mAutoclickIndicatorScheduler = new AutoclickIndicatorScheduler(handler);
         mAutoclickIndicatorView = new AutoclickIndicatorView(mContext);
 
-        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
-        layoutParams.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
-        layoutParams.flags =
-                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-        layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-        layoutParams.setFitInsetsTypes(0);
-        layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        layoutParams.format = PixelFormat.TRANSLUCENT;
-        layoutParams.setTitle(AutoclickIndicatorView.class.getSimpleName());
-        layoutParams.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
-
         mWindowManager = mContext.getSystemService(WindowManager.class);
-        mWindowManager.addView(mAutoclickIndicatorView, layoutParams);
+        mWindowManager.addView(mAutoclickIndicatorView, mAutoclickIndicatorView.getLayoutParams());
     }
 
     @Override
@@ -209,6 +201,11 @@
         private final Uri mAutoclickCursorAreaSizeSettingUri =
                 Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE);
 
+        /** URI used to identify ignore minor cursor movement setting with content resolver. */
+        private final Uri mAutoclickIgnoreMinorCursorMovementSettingUri =
+                Settings.Secure.getUriFor(
+                        Settings.Secure.ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT);
+
         private ContentResolver mContentResolver;
         private ClickScheduler mClickScheduler;
         private AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
@@ -247,18 +244,32 @@
             mContentResolver = contentResolver;
             mClickScheduler = clickScheduler;
             mAutoclickIndicatorScheduler = autoclickIndicatorScheduler;
-            mContentResolver.registerContentObserver(mAutoclickDelaySettingUri, false, this,
+            mContentResolver.registerContentObserver(
+                    mAutoclickDelaySettingUri,
+                    /* notifyForDescendants= */ false,
+                    /* observer= */ this,
                     mUserId);
 
             // Initialize mClickScheduler's initial delay value.
-            onChange(true, mAutoclickDelaySettingUri);
+            onChange(/* selfChange= */ true, mAutoclickDelaySettingUri);
 
             if (Flags.enableAutoclickIndicator()) {
                 // Register observer to listen to cursor area size setting change.
                 mContentResolver.registerContentObserver(
-                        mAutoclickCursorAreaSizeSettingUri, false, this, mUserId);
+                        mAutoclickCursorAreaSizeSettingUri,
+                        /* notifyForDescendants= */ false,
+                        /* observer= */ this,
+                        mUserId);
                 // Initialize mAutoclickIndicatorView's initial size.
-                onChange(true, mAutoclickCursorAreaSizeSettingUri);
+                onChange(/* selfChange= */ true, mAutoclickCursorAreaSizeSettingUri);
+
+                // Register observer to listen to ignore minor cursor movement setting change.
+                mContentResolver.registerContentObserver(
+                        mAutoclickIgnoreMinorCursorMovementSettingUri,
+                        /* notifyForDescendants= */ false,
+                        /* observer= */ this,
+                        mUserId);
+                onChange(/* selfChange= */ true, mAutoclickIgnoreMinorCursorMovementSettingUri);
             }
         }
 
@@ -279,21 +290,41 @@
         @Override
         public void onChange(boolean selfChange, Uri uri) {
             if (mAutoclickDelaySettingUri.equals(uri)) {
-                int delay = Settings.Secure.getIntForUser(
-                        mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
-                        AccessibilityManager.AUTOCLICK_DELAY_DEFAULT, mUserId);
-                mClickScheduler.updateDelay(delay);
-            }
-            if (Flags.enableAutoclickIndicator()
-                    && mAutoclickCursorAreaSizeSettingUri.equals(uri)) {
-                int size =
+                int delay =
                         Settings.Secure.getIntForUser(
                                 mContentResolver,
-                                Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
-                                AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT,
+                                Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
+                                AUTOCLICK_DELAY_DEFAULT,
                                 mUserId);
-                if (mAutoclickIndicatorScheduler != null) {
-                    mAutoclickIndicatorScheduler.updateCursorAreaSize(size);
+                mClickScheduler.updateDelay(delay);
+            }
+
+            if (Flags.enableAutoclickIndicator()) {
+                if (mAutoclickCursorAreaSizeSettingUri.equals(uri)) {
+                    int size =
+                            Settings.Secure.getIntForUser(
+                                    mContentResolver,
+                                    Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
+                                    AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT,
+                                    mUserId);
+                    if (mAutoclickIndicatorScheduler != null) {
+                        mAutoclickIndicatorScheduler.updateCursorAreaSize(size);
+                    }
+                    mClickScheduler.updateMovementSlope(size);
+                }
+
+                if (mAutoclickIgnoreMinorCursorMovementSettingUri.equals(uri)) {
+                    boolean ignoreMinorCursorMovement =
+                            Settings.Secure.getIntForUser(
+                                    mContentResolver,
+                                    Settings.Secure
+                                            .ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT,
+                                    AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT
+                                            ? AccessibilityUtils.State.ON
+                                            : AccessibilityUtils.State.OFF,
+                                    mUserId)
+                            == AccessibilityUtils.State.ON;
+                    mClickScheduler.setIgnoreMinorCursorMovement(ignoreMinorCursorMovement);
                 }
             }
         }
@@ -365,11 +396,16 @@
     @VisibleForTesting
     final class ClickScheduler implements Runnable {
         /**
-         * Minimal distance pointer has to move relative to anchor in order for movement not to be
-         * discarded as noise. Anchor is the position of the last MOVE event that was not considered
-         * noise.
+         * Default minimal distance pointer has to move relative to anchor in order for movement not
+         * to be discarded as noise. Anchor is the position of the last MOVE event that was not
+         * considered noise.
          */
-        private static final double MOVEMENT_SLOPE = 20f;
+        private static final double DEFAULT_MOVEMENT_SLOPE = 20f;
+
+        private double mMovementSlope = DEFAULT_MOVEMENT_SLOPE;
+
+        /** Whether the minor cursor movement should be ignored. */
+        private boolean mIgnoreMinorCursorMovement = AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT;
 
         /** Whether there is pending click. */
         private boolean mActive;
@@ -553,7 +589,19 @@
             float deltaX = mAnchorCoords.x - event.getX(pointerIndex);
             float deltaY = mAnchorCoords.y - event.getY(pointerIndex);
             double delta = Math.hypot(deltaX, deltaY);
-            return delta > MOVEMENT_SLOPE;
+            double slope =
+                    ((Flags.enableAutoclickIndicator() && mIgnoreMinorCursorMovement)
+                            ? mMovementSlope
+                            : DEFAULT_MOVEMENT_SLOPE);
+            return delta > slope;
+        }
+
+        public void setIgnoreMinorCursorMovement(boolean ignoreMinorCursorMovement) {
+            mIgnoreMinorCursorMovement = ignoreMinorCursorMovement;
+        }
+
+        private void updateMovementSlope(double slope) {
+            mMovementSlope = slope;
         }
 
         /**
@@ -581,18 +629,30 @@
 
             final long now = SystemClock.uptimeMillis();
 
-            MotionEvent downEvent = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1,
-                    mTempPointerProperties, mTempPointerCoords, mMetaState,
-                    MotionEvent.BUTTON_PRIMARY, 1.0f, 1.0f, mLastMotionEvent.getDeviceId(), 0,
-                    mLastMotionEvent.getSource(), mLastMotionEvent.getFlags());
+            MotionEvent downEvent =
+                    MotionEvent.obtain(
+                            /* downTime= */ now,
+                            /* eventTime= */ now,
+                            MotionEvent.ACTION_DOWN,
+                            /* pointerCount= */ 1,
+                            mTempPointerProperties,
+                            mTempPointerCoords,
+                            mMetaState,
+                            BUTTON_PRIMARY,
+                            /* xPrecision= */ 1.0f,
+                            /* yPrecision= */ 1.0f,
+                            mLastMotionEvent.getDeviceId(),
+                            /* edgeFlags= */ 0,
+                            mLastMotionEvent.getSource(),
+                            mLastMotionEvent.getFlags());
 
             MotionEvent pressEvent = MotionEvent.obtain(downEvent);
             pressEvent.setAction(MotionEvent.ACTION_BUTTON_PRESS);
-            pressEvent.setActionButton(MotionEvent.BUTTON_PRIMARY);
+            pressEvent.setActionButton(BUTTON_PRIMARY);
 
             MotionEvent releaseEvent = MotionEvent.obtain(downEvent);
             releaseEvent.setAction(MotionEvent.ACTION_BUTTON_RELEASE);
-            releaseEvent.setActionButton(MotionEvent.BUTTON_PRIMARY);
+            releaseEvent.setActionButton(BUTTON_PRIMARY);
             releaseEvent.setButtonState(0);
 
             MotionEvent upEvent = MotionEvent.obtain(downEvent);
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
similarity index 75%
rename from services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
rename to services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
index f87dcdb2..557e1b2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
@@ -14,17 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.server.accessibility;
+package com.android.server.accessibility.autoclick;
 
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.accessibility.AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
 
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.PixelFormat;
 import android.graphics.RectF;
 import android.util.DisplayMetrics;
 import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.LinearInterpolator;
 
@@ -81,6 +85,26 @@
         mRingRect = new RectF();
     }
 
+    /**
+     * Retrieves the layout params for AutoclickIndicatorView, used when it's added to the Window
+     * Manager.
+     */
+    public final WindowManager.LayoutParams getLayoutParams() {
+        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
+        layoutParams.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+        layoutParams.flags =
+                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+        layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+        layoutParams.setFitInsetsTypes(WindowInsets.Type.statusBars());
+        layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        layoutParams.format = PixelFormat.TRANSLUCENT;
+        layoutParams.setTitle(AutoclickIndicatorView.class.getSimpleName());
+        layoutParams.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
+        return layoutParams;
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 75ec8ea..e757dd5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -36,6 +36,8 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -48,11 +50,13 @@
 import android.util.SparseLongArray;
 import android.util.TypedValue;
 import android.view.Display;
+import android.view.ViewConfiguration;
 import android.view.accessibility.MagnificationAnimationCallback;
 
 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.wm.WindowManagerInternal;
@@ -111,6 +115,19 @@
 
     private final Executor mBackgroundExecutor;
 
+    private final Handler mHandler;
+    private @PanDirection int mActivePanDirection = PAN_DIRECTION_DOWN;
+    private int mActivePanDisplay = Display.INVALID_DISPLAY;
+    private boolean mRepeatKeysEnabled = true;
+
+    private @ZoomDirection int mActiveZoomDirection = ZOOM_DIRECTION_IN;
+    private int mActiveZoomDisplay = Display.INVALID_DISPLAY;
+
+    private int mInitialKeyboardRepeatIntervalMs =
+            ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT;
+    @VisibleForTesting
+    public static final int KEYBOARD_REPEAT_INTERVAL_MS = 60;
+
     @GuardedBy("mLock")
     private final SparseIntArray mCurrentMagnificationModeArray = new SparseIntArray();
     @GuardedBy("mLock")
@@ -287,12 +304,13 @@
 
     public MagnificationController(AccessibilityManagerService ams, Object lock,
             Context context, MagnificationScaleProvider scaleProvider,
-            Executor backgroundExecutor) {
+            Executor backgroundExecutor, Looper looper) {
         mAms = ams;
         mLock = lock;
         mContext = context;
         mScaleProvider = scaleProvider;
         mBackgroundExecutor = backgroundExecutor;
+        mHandler = new Handler(looper);
         LocalServices.getService(WindowManagerInternal.class)
                 .getAccessibilityController().setUiChangesForAccessibilityCallbacks(this);
         mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
@@ -309,8 +327,8 @@
     public MagnificationController(AccessibilityManagerService ams, Object lock,
             Context context, FullScreenMagnificationController fullScreenMagnificationController,
             MagnificationConnectionManager magnificationConnectionManager,
-            MagnificationScaleProvider scaleProvider, Executor backgroundExecutor) {
-        this(ams, lock, context, scaleProvider, backgroundExecutor);
+            MagnificationScaleProvider scaleProvider, Executor backgroundExecutor, Looper looper) {
+        this(ams, lock, context, scaleProvider, backgroundExecutor, looper);
         mFullScreenMagnificationController = fullScreenMagnificationController;
         mMagnificationConnectionManager = magnificationConnectionManager;
     }
@@ -354,27 +372,73 @@
         // pan diagonally) by decreasing diagonal movement by sqrt(2) to make it appear the same
         // speed as non-diagonal movement.
         panMagnificationByStep(displayId, direction);
+        mActivePanDirection = direction;
+        mActivePanDisplay = displayId;
+        if (mRepeatKeysEnabled) {
+            mHandler.sendMessageDelayed(
+                    PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this),
+                    mInitialKeyboardRepeatIntervalMs);
+        }
     }
 
     @Override
     public void onPanMagnificationStop(int displayId,
             @MagnificationController.PanDirection int direction) {
-        // TODO(b/388847283): Handle held key gestures, which can be used
-        // for continuous scaling and panning, until they are released.
-
+        if (direction == mActivePanDirection) {
+            mActivePanDisplay = Display.INVALID_DISPLAY;
+        }
     }
 
     @Override
     public void onScaleMagnificationStart(int displayId,
             @MagnificationController.ZoomDirection int direction) {
         scaleMagnificationByStep(displayId, direction);
+        mActiveZoomDirection = direction;
+        mActiveZoomDisplay = displayId;
+        if (mRepeatKeysEnabled) {
+            mHandler.sendMessageDelayed(
+                    PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, this),
+                    mInitialKeyboardRepeatIntervalMs);
+        }
     }
 
     @Override
     public void onScaleMagnificationStop(int displayId,
             @MagnificationController.ZoomDirection int direction) {
-        // TODO(b/388847283): Handle held key gestures, which can be used
-        // for continuous scaling and panning, until they are released.
+        if (direction == mActiveZoomDirection) {
+            mActiveZoomDisplay = Display.INVALID_DISPLAY;
+        }
+    }
+
+    private void maybeContinuePan() {
+        if (mActivePanDisplay != Display.INVALID_DISPLAY) {
+            panMagnificationByStep(mActivePanDisplay, mActivePanDirection);
+            mHandler.sendMessageDelayed(
+                    PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this),
+                    KEYBOARD_REPEAT_INTERVAL_MS);
+        }
+    }
+
+    private void maybeContinueZoom() {
+        if (mActiveZoomDisplay != Display.INVALID_DISPLAY) {
+            scaleMagnificationByStep(mActiveZoomDisplay, mActiveZoomDirection);
+            mHandler.sendMessageDelayed(
+                    PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, this),
+                    KEYBOARD_REPEAT_INTERVAL_MS);
+        }
+    }
+
+    public void setRepeatKeysEnabled(boolean isRepeatKeysEnabled) {
+        mRepeatKeysEnabled = isRepeatKeysEnabled;
+    }
+
+    public void setRepeatKeysTimeoutMs(int repeatKeysTimeoutMs) {
+        mInitialKeyboardRepeatIntervalMs = repeatKeysTimeoutMs;
+    }
+
+    @VisibleForTesting
+    public int getInitialKeyboardRepeatIntervalMs() {
+        return mInitialKeyboardRepeatIntervalMs;
     }
 
     private void handleUserInteractionChanged(int displayId, int mode) {
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
index 6191767..98ef974 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
@@ -60,9 +60,7 @@
      * Validates that the caller can execute the specified app function.
      *
      * <p>The caller can execute if the app function's package name is the same as the caller's
-     * package or the caller has either {@link Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} or
-     * {@link Manifest.permission#EXECUTE_APP_FUNCTIONS} granted. In some cases, app functions can
-     * still opt-out of caller having {@link Manifest.permission#EXECUTE_APP_FUNCTIONS}.
+     * package or the caller has the {@link Manifest.permission#EXECUTE_APP_FUNCTIONS} granted.
      *
      * @param callingUid The calling uid.
      * @param callingPid The calling pid.
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
index 69481c3..fe163d7 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
@@ -18,7 +18,6 @@
 
 import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_STATIC_METADATA_DB;
 import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_STATIC_NAMESPACE;
-import static android.app.appfunctions.AppFunctionStaticMetadataHelper.STATIC_PROPERTY_RESTRICT_CALLERS_WITH_EXECUTE_APP_FUNCTIONS;
 import static android.app.appfunctions.AppFunctionStaticMetadataHelper.getDocumentIdForAppFunction;
 
 import static com.android.server.appfunctions.AppFunctionExecutors.THREAD_POOL_EXECUTOR;
@@ -84,12 +83,7 @@
     }
 
     @Override
-    @RequiresPermission(
-            anyOf = {
-                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
-                Manifest.permission.EXECUTE_APP_FUNCTIONS
-            },
-            conditional = true)
+    @RequiresPermission(Manifest.permission.EXECUTE_APP_FUNCTIONS)
     public AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
             int callingUid,
             int callingPid,
@@ -101,17 +95,6 @@
             return AndroidFuture.completedFuture(true);
         }
 
-        boolean hasTrustedExecutionPermission =
-                mContext.checkPermission(
-                                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
-                                callingPid,
-                                callingUid)
-                        == PackageManager.PERMISSION_GRANTED;
-
-        if (hasTrustedExecutionPermission) {
-            return AndroidFuture.completedFuture(true);
-        }
-
         boolean hasExecutionPermission =
                 mContext.checkPermission(
                                 Manifest.permission.EXECUTE_APP_FUNCTIONS, callingPid, callingUid)
@@ -138,7 +121,8 @@
                                 .build())
                 .thenApply(
                         batchResult -> getGenericDocumentFromBatchResult(batchResult, documentId))
-                .thenApply(document -> !getRestrictCallersWithExecuteAppFunctionsProperty(document))
+                // At this point, already checked the app has the permission.
+                .thenApply(document -> true)
                 .whenComplete(
                         (result, throwable) -> {
                             futureAppSearchSession.close();
@@ -160,12 +144,6 @@
                         + failedResult.getErrorMessage());
     }
 
-    private static boolean getRestrictCallersWithExecuteAppFunctionsProperty(
-            GenericDocument genericDocument) {
-        return genericDocument.getPropertyBoolean(
-                STATIC_PROPERTY_RESTRICT_CALLERS_WITH_EXECUTE_APP_FUNCTIONS);
-    }
-
     @Override
     public boolean verifyEnterprisePolicyIsAllowed(
             @NonNull UserHandle callingUser, @NonNull UserHandle targetUser) {
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
index cc73288..9d13e37 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
@@ -78,7 +78,6 @@
     // Hidden constants in {@link SetSchemaRequest} that restricts runtime metadata visibility
     // by permissions.
     public static final int EXECUTE_APP_FUNCTIONS = 9;
-    public static final int EXECUTE_APP_FUNCTIONS_TRUSTED = 10;
 
     public MetadataSyncAdapter(
             @NonNull PackageManager packageManager, @NonNull AppSearchManager appSearchManager) {
@@ -281,8 +280,6 @@
                     new PackageIdentifier(packageName, packageCert));
             setSchemaRequestBuilder.addRequiredPermissionsForSchemaTypeVisibility(
                     runtimeMetadataSchema.getSchemaType(), Set.of(EXECUTE_APP_FUNCTIONS));
-            setSchemaRequestBuilder.addRequiredPermissionsForSchemaTypeVisibility(
-                    runtimeMetadataSchema.getSchemaType(), Set.of(EXECUTE_APP_FUNCTIONS_TRUSTED));
         }
         return setSchemaRequestBuilder.build();
     }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 8ef44ad..d47aab0 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1095,6 +1095,10 @@
             proto.write(WidgetProto.MAX_HEIGHT,
                 widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 0));
         }
+        if (widget.views != null) {
+            proto.write(WidgetProto.VIEWS_BITMAP_MEMORY,
+                    widget.views.estimateTotalBitmapMemoryUsage());
+        }
         proto.end(token);
     }
 
@@ -2846,7 +2850,7 @@
                 // For a full update we replace the RemoteViews completely.
                 widget.views = views;
             }
-            int memoryUsage;
+            long memoryUsage;
             if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) &&
                     (widget.views != null) &&
                     ((memoryUsage = widget.views.estimateMemoryUsage()) > mMaxWidgetBitmapMemory)) {
@@ -3503,6 +3507,8 @@
         }
         if (widget.views != null) {
             pw.print("    views="); pw.println(widget.views);
+            pw.print("    views_bitmap_memory=");
+            pw.println(widget.views.estimateTotalBitmapMemoryUsage());
         }
     }
 
@@ -4667,12 +4673,6 @@
                         keep.add(providerId);
                         // Use the new AppWidgetProviderInfo.
                         provider.setPartialInfoLocked(info);
-                        // Clear old previews
-                        if (remoteViewsProto()) {
-                            clearGeneratedPreviewsAsync(provider);
-                        } else {
-                            provider.clearGeneratedPreviewsLocked();
-                        }
                         // If it's enabled
                         final int M = provider.widgets.size();
                         if (M > 0) {
@@ -5098,6 +5098,10 @@
         AndroidFuture<RemoteViews> result = new AndroidFuture<>();
         mSavePreviewsHandler.post(() -> {
             SparseArray<RemoteViews> previews = loadGeneratedPreviews(provider);
+            if (previews.size() == 0 && provider.info.generatedPreviewCategories != 0) {
+                // Failed to read previews from file, clear the file and update providers.
+                saveGeneratedPreviews(provider, previews, /* notify= */ true);
+            }
             for (int i = 0; i < previews.size(); i++) {
                 if ((widgetCategory & previews.keyAt(i)) != 0) {
                     result.complete(previews.valueAt(i));
@@ -5216,8 +5220,14 @@
                 continue;
             }
             ProtoInputStream input = new ProtoInputStream(previewsFile.readFully());
-            provider.info.generatedPreviewCategories = readGeneratedPreviewCategoriesFromProto(
-                    input);
+            try {
+                provider.info.generatedPreviewCategories = readGeneratedPreviewCategoriesFromProto(
+                        input);
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to read generated previews from file for " + provider, e);
+                previewsFile.delete();
+                provider.info.generatedPreviewCategories = 0;
+            }
             if (DEBUG) {
                 Slog.i(TAG, TextUtils.formatSimple(
                         "loadGeneratedPreviewCategoriesLocked %d %s categories %d", profileId,
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 25494fc..c68e5495 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,7 +19,6 @@
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
 import static android.service.autofill.Flags.fixGetAutofillComponent;
-import static android.service.autofill.Flags.improveFillDialogAconfig;
 import static android.view.autofill.AutofillManager.MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
 
@@ -71,7 +70,6 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
-import android.view.InsetsController;
 import android.view.autofill.AutofillFeatureFlags;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
@@ -98,7 +96,6 @@
 import com.android.server.infra.AbstractMasterSystemService;
 import com.android.server.infra.FrameworkResourcesServiceNameResolver;
 import com.android.server.infra.SecureSettingsServiceNameResolver;
-import com.android.server.wm.WindowManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -246,8 +243,6 @@
 
     private static final boolean DEFAULT_PCC_USE_FALLBACK = true;
 
-    private static final boolean DBG = false;
-
     public AutofillManagerService(Context context) {
         super(context,
                 new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE),
@@ -306,79 +301,8 @@
             mCredentialAutofillService = null;
             Slog.w(TAG, "Invalid CredentialAutofillService");
         }
-
-        if (improveFillDialogAconfig()) {
-            WindowManagerInternal windowManagerInternal = LocalServices.getService(
-                    WindowManagerInternal.class);
-            WindowManagerInternal.ImeInsetsAnimationChangeListener
-                    imeInsetsAnimationChangeListener =
-                    new WindowManagerInternal.ImeInsetsAnimationChangeListener() {
-                        @Override
-                        public void onAnimationStart(
-                                @InsetsController.AnimationType int animationType, int userId) {
-                            if (DBG) {
-                                Slog.e(TAG,
-                                        "onAnimationStart() notifyImeAnimationStart() "
-                                                + "animationType:"
-                                                + String.valueOf(animationType));
-                            }
-                            synchronized (mLock) {
-
-                                // We are mostly interested in animations that show up the IME
-                                if (animationType == InsetsController.ANIMATION_TYPE_HIDE) {
-                                    // IME is going away
-                                    mIsImeShowing = false;
-                                }
-                                if (animationType != InsetsController.ANIMATION_TYPE_SHOW) {
-                                    return;
-                                }
-                                mIsImeShowing = true;
-                                mImeAnimatingWhileShowingUp = true;
-                                final AutofillManagerServiceImpl service =
-                                        peekServiceForUserWithLocalBinderIdentityLocked(userId);
-                                if (service != null) {
-                                    service.notifyImeAnimationStart();
-                                } else if (sVerbose) {
-                                    Slog.v(TAG,
-                                            "notifyImeAnimationStart(): no service for " + userId);
-                                }
-                            }
-                        }
-
-                        @Override
-                        public void onAnimationEnd(
-                                @InsetsController.AnimationType int animationType, int userId) {
-                            if (DBG) {
-                                Slog.e(TAG,
-                                        "onAnimationEnd() notifyImeAnimationEnd() "
-                                                + "animationType:"
-                                                + String.valueOf(animationType));
-                            }
-                            // We are only interested in animations that show up the IME
-                            if (animationType != InsetsController.ANIMATION_TYPE_SHOW) {
-                                return;
-                            }
-                            mImeAnimatingWhileShowingUp = false;
-                            synchronized (mLock) {
-                                final AutofillManagerServiceImpl service =
-                                        peekServiceForUserWithLocalBinderIdentityLocked(userId);
-                                if (service != null) {
-                                    service.notifyImeAnimationEnd();
-                                } else if (sVerbose) {
-                                    Slog.v(TAG, "notifyImeAnimationEnd(): no service for "
-                                            + userId);
-                                }
-                            }
-                        }
-                    };
-            windowManagerInternal.setImeInsetsAnimationChangeListener(
-                    imeInsetsAnimationChangeListener);
-        }
     }
 
-    public boolean mImeAnimatingWhileShowingUp = false;
-    public boolean mIsImeShowing = false;
-
     @Override // from AbstractMasterSystemService
     protected String getServiceSettingsProperty() {
         return Settings.Secure.AUTOFILL_SERVICE;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index eda6233..b39b5b1 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -209,11 +209,6 @@
 
     private final DisabledInfoCache mDisabledInfoCache;
 
-    // Tracks active session id. There is no guarantee that such a session exists. For eg, if the
-    // session is destroyed, the id may no longer be valid. We don't update the state in all the
-    // cases.
-    private int mActiveSessionId = NO_SESSION;
-
     AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
             LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
             AutofillCompatState autofillCompatState,
@@ -392,7 +387,6 @@
             @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
             @NonNull ComponentName clientActivity, boolean compatMode,
             boolean bindInstantServiceAllowed, int flags) {
-        mActiveSessionId = NO_SESSION;
         // FLAG_AUGMENTED_AUTOFILL_REQUEST is set in the flags when standard autofill is disabled
         // but the package is allowlisted for augmented autofill
         boolean forAugmentedAutofillOnly = (flags
@@ -451,7 +445,6 @@
         if (newSession == null) {
             return NO_SESSION;
         }
-        mActiveSessionId = newSession.id;
 
         // Service can be null when it's only for augmented autofill
         String servicePackageName = mInfo == null ? null : mInfo.getServiceInfo().packageName;
@@ -755,7 +748,6 @@
                     Slog.d(TAG, "restarting session " + sessionId + " due to manual request on "
                             + autofillId);
                 }
-                mActiveSessionId = sessionId;
                 return true;
             }
             if (sVerbose) {
@@ -765,8 +757,6 @@
             return false;
         }
 
-
-        mActiveSessionId = sessionId;
         session.updateLocked(autofillId, virtualBounds, value, action, flags);
         return false;
     }
@@ -886,54 +876,21 @@
     }
 
     @GuardedBy("mLock")
-    public void notifyImeAnimationStart() {
-        if (!isEnabledLocked()) {
-            Slog.wtf(TAG, "Service not enabled");
-            return;
-        }
-        final Session session = mSessions.get(mActiveSessionId);
-        if (session == null) {
-            Slog.v(TAG, "notifyImeAnimationEnd(): no session for " + mActiveSessionId);
-            return;
-        }
-        session.notifyImeAnimationStart(SystemClock.elapsedRealtime());
-    }
-
-    @GuardedBy("mLock")
     public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int uid) {
         if (!isEnabledLocked()) {
             Slog.wtf(TAG, "Service not enabled");
             return;
         }
         final Session session = mSessions.get(sessionId);
-        if (session == null) {
+        if (session == null || uid != session.uid) {
             Slog.v(TAG, "notifyImeAnimationEnd(): no session for "
                     + sessionId + "(" + uid + ")");
             return;
         }
-        if (uid != session.uid) {
-            Slog.v(TAG, "notifyImeAnimationEnd(): Mismatched session id's "
-                    + sessionId + "(" + uid + ")");
-            return;
-        }
         session.notifyImeAnimationEnd(endTimeMs);
     }
 
     @GuardedBy("mLock")
-    public void notifyImeAnimationEnd() {
-        if (!isEnabledLocked()) {
-            Slog.wtf(TAG, "Service not enabled");
-            return;
-        }
-        final Session session = mSessions.get(mActiveSessionId);
-        if (session == null) {
-            Slog.v(TAG, "notifyImeAnimationEnd(): no session for " + mActiveSessionId);
-            return;
-        }
-        session.notifyImeAnimationEnd(SystemClock.elapsedRealtime());
-    }
-
-    @GuardedBy("mLock")
     @Override // from PerUserSystemService
     protected void handlePackageUpdateLocked(@NonNull String packageName) {
         final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
diff --git a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
index d8fbde4..9a353fb 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
@@ -76,7 +76,9 @@
     @Nullable
     private BackupAgentConnection mCurrentConnection;
 
+    @GuardedBy("mAgentConnectLock")
     private final ArraySet<String> mRestoreNoRestrictedModePackages = new ArraySet<>();
+    @GuardedBy("mAgentConnectLock")
     private final ArraySet<String> mBackupNoRestrictedModePackages = new ArraySet<>();
 
     private final IActivityManager mActivityManager;
@@ -322,14 +324,16 @@
      */
     public void setNoRestrictedModePackages(Set<String> packageNames,
             @BackupAnnotations.OperationType int opType) {
-        if (opType == BackupAnnotations.OperationType.BACKUP) {
-            mBackupNoRestrictedModePackages.clear();
-            mBackupNoRestrictedModePackages.addAll(packageNames);
-        } else if (opType == BackupAnnotations.OperationType.RESTORE) {
-            mRestoreNoRestrictedModePackages.clear();
-            mRestoreNoRestrictedModePackages.addAll(packageNames);
-        } else {
-            throw new IllegalArgumentException("opType must be BACKUP or RESTORE");
+        synchronized (mAgentConnectLock) {
+            if (opType == BackupAnnotations.OperationType.BACKUP) {
+                mBackupNoRestrictedModePackages.clear();
+                mBackupNoRestrictedModePackages.addAll(packageNames);
+            } else if (opType == BackupAnnotations.OperationType.RESTORE) {
+                mRestoreNoRestrictedModePackages.clear();
+                mRestoreNoRestrictedModePackages.addAll(packageNames);
+            } else {
+                throw new IllegalArgumentException("opType must be BACKUP or RESTORE");
+            }
         }
     }
 
@@ -338,8 +342,10 @@
      * restore.
      */
     public void clearNoRestrictedModePackages() {
-        mBackupNoRestrictedModePackages.clear();
-        mRestoreNoRestrictedModePackages.clear();
+        synchronized (mAgentConnectLock) {
+            mBackupNoRestrictedModePackages.clear();
+            mRestoreNoRestrictedModePackages.clear();
+        }
     }
 
     /**
@@ -353,6 +359,7 @@
      * {@link #mRestoreNoRestrictedModePackages} so this method will immediately return without
      * any IPC to the transport.
      */
+    @GuardedBy("mAgentConnectLock")
     private boolean shouldUseRestrictedBackupModeForPackage(
             @BackupAnnotations.OperationType int mode, String packageName) {
         // Key/Value apps are never put in restricted mode.
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index a37b2b9..521f676 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -47,6 +47,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
+import android.app.KeyguardManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.ecm.EnhancedConfirmationManager;
@@ -302,8 +303,17 @@
             enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName,
                     "create associations");
 
-            mAssociationRequestsProcessor.processNewAssociationRequest(
-                    request, packageName, userId, callback);
+            if (request.isSkipRoleGrant()) {
+                checkCallerCanSkipRoleGrant();
+                mAssociationRequestsProcessor.createAssociation(userId, packageName,
+                        /* macAddress= */ null, request.getDisplayName(),
+                        request.getDeviceProfile(), /* associatedDevice= */ null,
+                        request.isSelfManaged(), callback, /* resultReceiver= */ null,
+                        request.getDeviceIcon(), /* skipRoleGrant= */ true);
+            } else {
+                mAssociationRequestsProcessor.processNewAssociationRequest(
+                        request, packageName, userId, callback);
+            }
         }
 
         @Override
@@ -612,7 +622,7 @@
 
         @Override
         public void enablePermissionsSync(int associationId) {
-            if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
+            if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
                 throw new SecurityException("Caller must be system UID");
             }
             mSystemDataTransferProcessor.enablePermissionsSync(associationId);
@@ -620,7 +630,7 @@
 
         @Override
         public void disablePermissionsSync(int associationId) {
-            if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
+            if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
                 throw new SecurityException("Caller must be system UID");
             }
             mSystemDataTransferProcessor.disablePermissionsSync(associationId);
@@ -628,7 +638,7 @@
 
         @Override
         public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
-            if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
+            if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
                 throw new SecurityException("Caller must be system UID");
             }
             return mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
@@ -669,7 +679,7 @@
 
             final MacAddress macAddressObj = MacAddress.fromString(macAddress);
             mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddressObj,
-                    null, null, null, false, null, null, null);
+                    null, null, null, false, null, null, null, false);
         }
 
         private void checkCanCallNotificationApi(String callingPackage, int userId) {
@@ -684,6 +694,19 @@
                     "App must have an association before calling this API");
         }
 
+        private void checkCallerCanSkipRoleGrant() {
+            final KeyguardManager keyguardManager =
+                    getContext().getSystemService(KeyguardManager.class);
+            if (keyguardManager != null && keyguardManager.isKeyguardSecure()) {
+                throw new SecurityException("Skipping CDM role grant requires insecure keyguard.");
+            }
+            if (getContext().checkCallingPermission(ASSOCIATE_COMPANION_DEVICES)
+                    != PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "Skipping CDM role grant requires ASSOCIATE_COMPANION_DEVICES permission.");
+            }
+        }
+
         @Override
         public boolean canPairWithoutPrompt(String packageName, String macAddress, int userId) {
             final AssociationInfo association =
@@ -704,7 +727,7 @@
 
         @Override
         public byte[] getBackupPayload(int userId) {
-            if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
+            if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
                 throw new SecurityException("Caller must be system");
             }
             return mBackupRestoreProcessor.getBackupPayload(userId);
@@ -712,7 +735,7 @@
 
         @Override
         public void applyRestoredPayload(byte[] payload, int userId) {
-            if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
+            if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
                 throw new SecurityException("Caller must be system");
             }
             mBackupRestoreProcessor.applyRestoredPayload(payload, userId);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 3508f2f..e7d1460 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -106,8 +106,9 @@
                     boolean selfManaged = getNextBooleanArg();
                     final MacAddress macAddress = MacAddress.fromString(address);
                     mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddress,
-                            deviceProfile, deviceProfile, /* associatedDevice */ null, selfManaged,
-                            /* callback */ null, /* resultReceiver */ null, /* deviceIcon */ null);
+                            deviceProfile, deviceProfile, /* associatedDevice= */ null, selfManaged,
+                            /* callback= */ null, /* resultReceiver= */ null,
+                            /* deviceIcon= */ null, /* skipRoleGrant= */ false);
                 }
                 break;
 
diff --git a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
index 899b302..8c2c63c 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -282,8 +282,8 @@
         Binder.withCleanCallingIdentity(() -> {
             createAssociation(userId, packageName, macAddress, request.getDisplayName(),
                     request.getDeviceProfile(), request.getAssociatedDevice(),
-                    request.isSelfManaged(),
-                    callback, resultReceiver, request.getDeviceIcon());
+                    request.isSelfManaged(), callback, resultReceiver, request.getDeviceIcon(),
+                    /* skipRoleGrant= */ false);
         });
     }
 
@@ -294,7 +294,8 @@
             @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
             @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
             boolean selfManaged, @Nullable IAssociationRequestCallback callback,
-            @Nullable ResultReceiver resultReceiver, @Nullable Icon deviceIcon) {
+            @Nullable ResultReceiver resultReceiver, @Nullable Icon deviceIcon,
+            boolean skipRoleGrant) {
         final int id = mAssociationStore.getNextId();
         final long timestamp = System.currentTimeMillis();
 
@@ -303,8 +304,17 @@
                 selfManaged, /* notifyOnDeviceNearby */ false, /* revoked */ false,
                 /* pending */ false, timestamp, Long.MAX_VALUE, /* systemDataSyncFlags */ 0,
                 deviceIcon, /* deviceId */ null);
-        // Add role holder for association (if specified) and add new association to store.
-        maybeGrantRoleAndStoreAssociation(association, callback, resultReceiver);
+
+        if (skipRoleGrant) {
+            Slog.i(TAG, "Created association for " + association.getDeviceProfile() + " and userId="
+                    + association.getUserId() + ", packageName="
+                    + association.getPackageName() + " without granting role");
+            mAssociationStore.addAssociation(association);
+            sendCallbackAndFinish(association, callback, resultReceiver);
+        } else {
+            // Add role holder for association (if specified) and add new association to store.
+            maybeGrantRoleAndStoreAssociation(association, callback, resultReceiver);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index 2412b01..d2db8f7 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -27,7 +27,6 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
-import android.os.Message;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.UEventObserver;
@@ -37,6 +36,7 @@
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
@@ -57,8 +57,6 @@
 final class DockObserver extends SystemService {
     private static final String TAG = "DockObserver";
 
-    private static final int MSG_DOCK_STATE_CHANGED = 0;
-
     private final PowerManager mPowerManager;
     private final PowerManager.WakeLock mWakeLock;
 
@@ -66,11 +64,16 @@
 
     private boolean mSystemReady;
 
+    @GuardedBy("mLock")
     private int mActualDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
 
+    @GuardedBy("mLock")
     private int mReportedDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+    @GuardedBy("mLock")
     private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
 
+    @GuardedBy("mLock")
     private boolean mUpdatesStopped;
 
     private final boolean mKeepDreamingWhenUnplugging;
@@ -79,6 +82,8 @@
     private final List<ExtconStateConfig> mExtconStateConfigs;
     private DeviceProvisionedObserver mDeviceProvisionedObserver;
 
+    private final DockObserverLocalService mDockObserverLocalService;
+
     static final class ExtconStateProvider {
         private final Map<String, String> mState;
 
@@ -180,26 +185,40 @@
                 ExtconInfo.EXTCON_DOCK
         });
 
-        if (!infos.isEmpty()) {
-            ExtconInfo info = infos.get(0);
-            Slog.i(TAG, "Found extcon info devPath: " + info.getDevicePath()
-                        + ", statePath: " + info.getStatePath());
+        synchronized (mLock) {
+            if (!infos.isEmpty()) {
+                ExtconInfo info = infos.get(0);
+                Slog.i(
+                        TAG,
+                        "Found extcon info devPath: "
+                                + info.getDevicePath()
+                                + ", statePath: "
+                                + info.getStatePath());
 
-            // set initial status
-            setDockStateFromProviderLocked(ExtconStateProvider.fromFile(info.getStatePath()));
-            mPreviousDockState = mActualDockState;
+                // set initial status
+                setDockStateFromProviderLocked(ExtconStateProvider.fromFile(info.getStatePath()));
+                mPreviousDockState = mActualDockState;
 
-            mExtconUEventObserver.startObserving(info);
-        } else {
-            Slog.i(TAG, "No extcon dock device found in this kernel.");
+                mExtconUEventObserver.startObserving(info);
+            } else {
+                Slog.i(TAG, "No extcon dock device found in this kernel.");
+            }
+        }
+
+        mDockObserverLocalService = new DockObserverLocalService();
+        LocalServices.addService(DockObserverInternal.class, mDockObserverLocalService);
+    }
+
+    public class DockObserverLocalService extends DockObserverInternal {
+        @Override
+        public int getActualDockState() {
+            return mActualDockState;
         }
     }
 
     @Override
     public void onStart() {
         publishBinderService(TAG, new BinderService());
-        // Logs dock state after setDockStateFromProviderLocked sets mReportedDockState
-        FrameworkStatsLog.write(FrameworkStatsLog.DOCK_STATE_CHANGED, mReportedDockState);
     }
 
     @Override
@@ -213,13 +232,15 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void updateIfDockedLocked() {
         // don't bother broadcasting undocked here
         if (mReportedDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
-            updateLocked();
+            postWakefulDockStateChange();
         }
     }
 
+    @GuardedBy("mLock")
     private void setActualDockStateLocked(int newState) {
         mActualDockState = newState;
         if (!mUpdatesStopped) {
@@ -227,16 +248,22 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void setDockStateLocked(int newState) {
         if (newState != mReportedDockState) {
             mReportedDockState = newState;
+            // Here is the place mReportedDockState is updated. Logs dock state for
+            // mReportedDockState here so we can report the dock state.
+            FrameworkStatsLog.write(FrameworkStatsLog.DOCK_STATE_CHANGED, mReportedDockState);
             if (mSystemReady) {
                 // Wake up immediately when docked or undocked unless prohibited from doing so.
                 if (allowWakeFromDock()) {
-                    mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+                    mPowerManager.wakeUp(
+                            SystemClock.uptimeMillis(),
+                            PowerManager.WAKE_REASON_DOCK,
                             "android.server:DOCK");
                 }
-                updateLocked();
+                postWakefulDockStateChange();
             }
         }
     }
@@ -250,9 +277,8 @@
                 Settings.Global.THEATER_MODE_ON, 0) == 0);
     }
 
-    private void updateLocked() {
-        mWakeLock.acquire();
-        mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED);
+    private void postWakefulDockStateChange() {
+        mHandler.post(mWakeLock.wrap(this::handleDockStateChange));
     }
 
     private void handleDockStateChange() {
@@ -335,17 +361,7 @@
         }
     }
 
-    private final Handler mHandler = new Handler(true /*async*/) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_DOCK_STATE_CHANGED:
-                    handleDockStateChange();
-                    mWakeLock.release();
-                    break;
-            }
-        }
-    };
+    private final Handler mHandler = new Handler(true /*async*/);
 
     private int getDockedStateExtraValue(ExtconStateProvider state) {
         for (ExtconStateConfig config : mExtconStateConfigs) {
@@ -373,6 +389,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void setDockStateFromProviderLocked(ExtconStateProvider provider) {
         int state = Intent.EXTRA_DOCK_STATE_UNDOCKED;
         if ("1".equals(provider.getValue("DOCK"))) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/services/core/java/com/android/server/DockObserverInternal.java
similarity index 64%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
copy to services/core/java/com/android/server/DockObserverInternal.java
index aa262f9..ea41ffd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/services/core/java/com/android/server/DockObserverInternal.java
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.utils
+package com.android.server;
 
-import android.app.Instrumentation
-
-object RecentTasksUtils {
-    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
-        instrumentation.uiAutomation.executeShellCommand(
-            "dumpsys activity service SystemUIService WMShell recents clearAll"
-        )
-    }
-}
\ No newline at end of file
+/**
+ * @hide Only for use within the system server.
+ */
+public abstract class DockObserverInternal {
+    /** Retrieve the current dock state from DockObserver. */
+    public abstract int getActualDockState();
+}
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 2b30c01..6f2ecd8 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -130,7 +130,7 @@
         if (mWipeExternalStorage) {
             // thr will be started at the end of this task.
             Slog.i(TAG, "Wiping external storage on async task");
-            new WipeDataTask(context, thr).execute();
+            new WipeDataTask(context, thr).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
         } else {
             Slog.i(TAG, "NOT wiping external storage; starting thread " + thr.getName());
             thr.start();
diff --git a/services/core/java/com/android/server/TradeInModeService.java b/services/core/java/com/android/server/TradeInModeService.java
index 70a0330..a696673 100644
--- a/services/core/java/com/android/server/TradeInModeService.java
+++ b/services/core/java/com/android/server/TradeInModeService.java
@@ -137,12 +137,13 @@
                 Slog.i(TAG, "Not starting trade-in mode, device is setup.");
                 return false;
             }
-            if (SystemProperties.getInt("ro.debuggable", 0) == 1) {
-                // We don't want to force adbd into TIM on debug builds.
-                Slog.e(TAG, "Not starting trade-in mode, device is debuggable.");
-                return false;
-            }
-            if (isAdbEnabled()) {
+            if (isDebuggable()) {
+                if (!isForceEnabledForTesting()) {
+                    // We don't want to force adbd into TIM on debug builds.
+                    Slog.e(TAG, "Not starting trade-in mode, device is debuggable.");
+                    return false;
+                }
+            } else if (isAdbEnabled()) {
                 Slog.e(TAG, "Not starting trade-in mode, adb is already enabled.");
                 return false;
             }
@@ -234,6 +235,10 @@
         return SystemProperties.getInt("ro.debuggable", 0) == 1;
     }
 
+    private boolean isForceEnabledForTesting() {
+        return SystemProperties.getInt("persist.adb.test_tradeinmode", 0) == 1;
+    }
+
     private boolean isAdbEnabled() {
         final ContentResolver cr = mContext.getContentResolver();
         return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 1;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0603c45..6ece265 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19397,13 +19397,13 @@
         if (((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0)
                 && intent.getExtras() != null && intent.getExtras().hasIntent()) {
             Slog.wtf(TAG,
-                    "[IntentRedirect] The intent does not have its nested keys collected as a "
+                    "[IntentRedirect Hardening] The intent does not have its nested keys collected as a "
                             + "preparation for creating intent creator tokens. Intent: "
                             + intent + "; creatorPackage: " + creatorPackage);
             if (preventIntentRedirectShowToastIfNestedKeysNotCollectedRW()) {
                 UiThread.getHandler().post(
                         () -> Toast.makeText(mContext,
-                                "Nested keys not collected. go/report-bug-intentRedir to report a"
+                                "Nested keys not collected, activity launch won't be blocked. go/report-bug-intentRedir to report a"
                                         + " bug", Toast.LENGTH_LONG).show());
             }
             if (preventIntentRedirectThrowExceptionIfNestedKeysNotCollected()) {
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index bfacfbb..bec5db7 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -317,7 +317,7 @@
                 Slog.w(TAG, "registerReceiverWithFeature: no app for " + caller);
                 return null;
             }
-            if (callerApp.info.uid != SYSTEM_UID
+            if (!UserHandle.isCore(callerApp.info.uid)
                     && !callerApp.getPkgList().containsKey(callerPackage)) {
                 throw new SecurityException("Given caller package " + callerPackage
                         + " is not running in process " + callerApp);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index d335529..ce526e5 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -572,6 +572,7 @@
         public long mTotalAnonMemFreedKBs;
         public long mSumOrigAnonRss;
         public double mMaxCompactEfficiency;
+        public double mMaxSwapEfficiency;
 
         // Cpu time
         public long mTotalCpuTimeMillis;
@@ -586,6 +587,10 @@
             if (compactEfficiency > mMaxCompactEfficiency) {
                 mMaxCompactEfficiency = compactEfficiency;
             }
+            final double swapEfficiency = anonRssSaved / (double) origAnonRss;
+            if (swapEfficiency > mMaxSwapEfficiency) {
+                mMaxSwapEfficiency = swapEfficiency;
+            }
             mTotalDeltaAnonRssKBs += anonRssSaved;
             mTotalZramConsumedKBs += zramConsumed;
             mTotalAnonMemFreedKBs += memFreed;
@@ -628,7 +633,11 @@
                 pw.println("    -----Memory Stats----");
                 pw.println("    Total Delta Anon RSS (KB) : " + mTotalDeltaAnonRssKBs);
                 pw.println("    Total Physical ZRAM Consumed (KB): " + mTotalZramConsumedKBs);
+                // Anon Mem Freed = Delta Anon RSS - ZRAM Consumed
                 pw.println("    Total Anon Memory Freed (KB): " + mTotalAnonMemFreedKBs);
+                pw.println("    Avg Swap Efficiency (KB) (Delta Anon RSS/Orig Anon RSS): "
+                        + (mTotalDeltaAnonRssKBs / (double) mSumOrigAnonRss));
+                pw.println("    Max Swap Efficiency: " + mMaxSwapEfficiency);
                 // This tells us how much anon memory we were able to free thanks to running
                 // compaction
                 pw.println("    Avg Compaction Efficiency (Anon Freed/Anon RSS): "
@@ -808,8 +817,9 @@
             pw.println("  Tracking last compaction stats for " + mLastCompactionStats.size()
                     + " processes.");
             pw.println("Last Compaction per process stats:");
-            pw.println("    (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
-                    + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+            pw.println("    (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs"
+                    + ",SwapEfficiency,CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,"
+                    + "oomAdjReason)");
             for (Map.Entry<Integer, SingleCompactionStats> entry :
                     mLastCompactionStats.entrySet()) {
                 SingleCompactionStats stats = entry.getValue();
@@ -818,7 +828,8 @@
             pw.println();
             pw.println("Last 20 Compactions Stats:");
             pw.println("    (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
-                    + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+                    + "SwapEfficiency,CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,"
+                    + "oomAdjReason)");
             for (SingleCompactionStats stats : mCompactionStatsHistory) {
                 stats.dump(pw);
             }
@@ -1779,6 +1790,8 @@
 
         double getCompactEfficiency() { return mAnonMemFreedKBs / (double) mOrigAnonRss; }
 
+        double getSwapEfficiency() { return mDeltaAnonRssKBs / (double) mOrigAnonRss; }
+
         double getCompactCost() {
             // mCpuTimeMillis / (anonMemFreedKBs/1024) and metric is in (ms/MB)
             return mCpuTimeMillis / (double) mAnonMemFreedKBs * 1024;
@@ -1791,7 +1804,8 @@
         @NeverCompile
         void dump(PrintWriter pw) {
             pw.println("    (" + mProcessName + "," + mSourceType.name() + "," + mDeltaAnonRssKBs
-                    + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + "," + getCompactEfficiency()
+                    + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + ","
+                    + getSwapEfficiency() + "," + getCompactEfficiency()
                     + "," + getCompactCost() + "," + mProcState + "," + mOomAdj + ","
                     + OomAdjuster.oomAdjReasonToString(mOomAdjReason) + ")");
         }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 3abcd4e..87f843b 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -440,6 +440,12 @@
     @GuardedBy("mService")
     private long mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME;
 
+    /**
+     * The oom score a client needs to be to raise a service with UI out of cache.
+     */
+    private static final int CACHING_UI_SERVICE_CLIENT_ADJ_THRESHOLD =
+            Flags.raiseBoundUiServiceThreshold() ? SERVICE_ADJ : PERCEPTIBLE_APP_ADJ;
+
     @VisibleForTesting
     public static class Injector {
         boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
@@ -2878,15 +2884,12 @@
                 }
             }
             if (adj > clientAdj) {
-                // If this process has recently shown UI, and
-                // the process that is binding to it is less
-                // important than being visible, then we don't
-                // care about the binding as much as we care
-                // about letting this process get into the LRU
-                // list to be killed and restarted if needed for
-                // memory.
+                // If this process has recently shown UI, and the process that is binding to it
+                // is less important than a state that can be actively running, then we don't
+                // care about the binding as much as we care about letting this process get into
+                // the LRU list to be killed and restarted if needed for memory.
                 if (state.hasShownUi() && !state.getCachedIsHomeProcess()
-                        && clientAdj > PERCEPTIBLE_APP_ADJ) {
+                        && clientAdj > CACHING_UI_SERVICE_CLIENT_ADJ_THRESHOLD) {
                     if (adj >= CACHED_APP_MIN_ADJ) {
                         adjType = "cch-bound-ui-services";
                     }
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 0b78901..2c0366e 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -24,11 +24,14 @@
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.window.flags.Flags.balClearAllowlistDuration;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
@@ -327,10 +330,11 @@
         mAllowBgActivityStartsForActivitySender.remove(token);
         mAllowBgActivityStartsForBroadcastSender.remove(token);
         mAllowBgActivityStartsForServiceSender.remove(token);
-        if (mAllowlistDuration != null) {
-            mAllowlistDuration.remove(token);
-            if (mAllowlistDuration.isEmpty()) {
-                mAllowlistDuration = null;
+        if (mAllowlistDuration != null && balClearAllowlistDuration()) {
+            TempAllowListDuration duration = mAllowlistDuration.get(token);
+            if (duration != null
+                    && duration.type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+                duration.type = TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index d9be471..e89889e 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -308,3 +308,14 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "raise_bound_ui_service_threshold"
+    namespace: "backstage_power"
+    is_fixed_read_only: true
+    description: "Raise the threshold OomAdjuster will drop a service with UI to cached."
+    bug: "391691057"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 8e09e3b..e8a2226 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -237,6 +237,13 @@
      */
     private static final int CURRENT_VERSION = 1;
 
+    /**
+     * The upper limit of total number of attributed op entries that can be returned in a binder
+     * transaction to avoid TransactionTooLargeException
+     */
+    private static final int NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD = 2000;
+
+
     private SensorPrivacyManager mSensorPrivacyManager;
 
     // Write at most every 30 minutes.
@@ -604,7 +611,7 @@
         }
     }
 
-    /** Returned from {@link #verifyAndGetBypass(int, String, String, String, boolean)}. */
+    /** Returned from {@link #verifyAndGetBypass(int, String, String, int, String, boolean)}. */
     private static final class PackageVerificationResult {
 
         final RestrictionBypass bypass;
@@ -1702,6 +1709,8 @@
                 Manifest.permission.GET_APP_OPS_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid())
                 == PackageManager.PERMISSION_GRANTED;
+        int totalAttributedOpEntryCount = 0;
+
         if (ops == null) {
             resOps = new ArrayList<>();
             for (int j = 0; j < pkgOps.size(); j++) {
@@ -1709,7 +1718,12 @@
                 if (opRestrictsRead(curOp.op) && !shouldReturnRestrictedAppOps) {
                     continue;
                 }
-                resOps.add(getOpEntryForResult(curOp, persistentDeviceId));
+                if (totalAttributedOpEntryCount > NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD) {
+                    break;
+                }
+                OpEntry opEntry = getOpEntryForResult(curOp, persistentDeviceId);
+                resOps.add(opEntry);
+                totalAttributedOpEntryCount += opEntry.getAttributedOpEntries().size();
             }
         } else {
             for (int j = 0; j < ops.length; j++) {
@@ -1721,10 +1735,21 @@
                     if (opRestrictsRead(curOp.op) && !shouldReturnRestrictedAppOps) {
                         continue;
                     }
-                    resOps.add(getOpEntryForResult(curOp, persistentDeviceId));
+                    if (totalAttributedOpEntryCount > NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD) {
+                        break;
+                    }
+                    OpEntry opEntry = getOpEntryForResult(curOp, persistentDeviceId);
+                    resOps.add(opEntry);
+                    totalAttributedOpEntryCount += opEntry.getAttributedOpEntries().size();
                 }
             }
         }
+
+        if (totalAttributedOpEntryCount > NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD) {
+            Slog.w(TAG, "The number of attributed op entries has exceeded the threshold. This "
+                    + "could be due to DoS attack from malicious apps. The result is throttled.");
+        }
+
         return resOps;
     }
 
@@ -3087,10 +3112,10 @@
     public int checkPackage(int uid, String packageName) {
         Objects.requireNonNull(packageName);
         try {
-            verifyAndGetBypass(uid, packageName, null, null, true);
+            verifyAndGetBypass(uid, packageName, null, Process.INVALID_UID, null, true);
             // When the caller is the system, it's possible that the packageName is the special
             // one (e.g., "root") which isn't actually existed.
-            if (resolveUid(packageName) == uid
+            if (resolveNonAppUid(packageName) == uid
                     || (isPackageExisted(packageName)
                             && !filterAppAccessUnlocked(packageName, UserHandle.getUserId(uid)))) {
                 return AppOpsManager.MODE_ALLOWED;
@@ -3306,7 +3331,7 @@
             boolean shouldCollectMessage, int notedCount) {
         PackageVerificationResult pvr;
         try {
-            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
+            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
             if (!pvr.isAttributionTagValid) {
                 attributionTag = null;
             }
@@ -3930,7 +3955,7 @@
             // Test if the proxied operation will succeed before starting the proxy operation
             final SyncNotedAppOp testProxiedOp = startOperationDryRun(code,
                     proxiedUid, resolvedProxiedPackageName, proxiedAttributionTag,
-                    proxiedVirtualDeviceId, resolvedProxyPackageName, proxiedFlags,
+                    proxiedVirtualDeviceId, proxyUid, resolvedProxyPackageName, proxiedFlags,
                     startIfModeDefault);
 
             if (!shouldStartForMode(testProxiedOp.getOpMode(), startIfModeDefault)) {
@@ -3970,7 +3995,7 @@
             int attributionChainId) {
         PackageVerificationResult pvr;
         try {
-            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
+            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
             if (!pvr.isAttributionTagValid) {
                 attributionTag = null;
             }
@@ -4097,11 +4122,11 @@
      */
     private SyncNotedAppOp startOperationDryRun(int code, int uid,
             @NonNull String packageName, @Nullable String attributionTag, int virtualDeviceId,
-            String proxyPackageName, @OpFlags int flags,
+            int proxyUid, String proxyPackageName, @OpFlags int flags,
             boolean startIfModeDefault) {
         PackageVerificationResult pvr;
         try {
-            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
+            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
             if (!pvr.isAttributionTagValid) {
                 attributionTag = null;
             }
@@ -4656,13 +4681,17 @@
     private boolean isSpecialPackage(int callingUid, @Nullable String packageName) {
         final String resolvedPackage = AppOpsManager.resolvePackageName(callingUid, packageName);
         return callingUid == Process.SYSTEM_UID
-                || resolveUid(resolvedPackage) != Process.INVALID_UID;
+                || resolveNonAppUid(resolvedPackage) != Process.INVALID_UID;
     }
 
     private boolean isCallerAndAttributionTrusted(@NonNull AttributionSource attributionSource) {
         if (attributionSource.getUid() != Binder.getCallingUid()
                 && attributionSource.isTrusted(mContext)) {
-            return true;
+            // if there is a next attribution source, it must be trusted, as well.
+            if (attributionSource.getNext() == null
+                    || attributionSource.getNext().isTrusted(mContext)) {
+                return true;
+            }
         }
         return mContext.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), null)
@@ -4757,19 +4786,20 @@
     }
 
     /**
-     * @see #verifyAndGetBypass(int, String, String, String, boolean)
+     * @see #verifyAndGetBypass(int, String, String, int, String, boolean)
      */
     private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
             @Nullable String attributionTag) {
-        return verifyAndGetBypass(uid, packageName, attributionTag, null);
+        return verifyAndGetBypass(uid, packageName, attributionTag, Process.INVALID_UID, null);
     }
 
     /**
-     * @see #verifyAndGetBypass(int, String, String, String, boolean)
+     * @see #verifyAndGetBypass(int, String, String, int, String, boolean)
      */
     private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
-            @Nullable String attributionTag, @Nullable String proxyPackageName) {
-        return verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName, false);
+            @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName) {
+        return verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName,
+                false);
     }
 
     /**
@@ -4780,14 +4810,15 @@
      * @param uid The uid the package belongs to
      * @param packageName The package the might belong to the uid
      * @param attributionTag attribution tag or {@code null} if no need to verify
-     * @param proxyPackageName The proxy package, from which the attribution tag is to be pulled
+     * @param proxyUid The proxy uid, from which the attribution tag is to be pulled
+     * @param proxyPackageName The proxy package, from which the attribution tag may be pulled
      * @param suppressErrorLogs Whether to print to logcat about nonmatching parameters
      *
      * @return PackageVerificationResult containing {@link RestrictionBypass} and whether the
      *         attribution tag is valid
      */
     private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
-            @Nullable String attributionTag, @Nullable String proxyPackageName,
+            @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName,
             boolean suppressErrorLogs) {
         if (uid == Process.ROOT_UID) {
             // For backwards compatibility, don't check package name for root UID.
@@ -4831,34 +4862,47 @@
 
         int callingUid = Binder.getCallingUid();
 
-        // Allow any attribution tag for resolvable uids
-        int pkgUid;
+        // Allow any attribution tag for resolvable, non-app uids
+        int nonAppUid;
         if (Objects.equals(packageName, "com.android.shell")) {
             // Special case for the shell which is a package but should be able
             // to bypass app attribution tag restrictions.
-            pkgUid = Process.SHELL_UID;
+            nonAppUid = Process.SHELL_UID;
         } else {
-            pkgUid = resolveUid(packageName);
+            nonAppUid = resolveNonAppUid(packageName);
         }
-        if (pkgUid != Process.INVALID_UID) {
-            if (pkgUid != UserHandle.getAppId(uid)) {
+        if (nonAppUid != Process.INVALID_UID) {
+            if (nonAppUid != UserHandle.getAppId(uid)) {
                 if (!suppressErrorLogs) {
                     Slog.e(TAG, "Bad call made by uid " + callingUid + ". "
-                            + "Package \"" + packageName + "\" does not belong to uid " + uid
-                            + ".");
+                                + "Package \"" + packageName + "\" does not belong to uid " + uid
+                                + ".");
                 }
-                String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not";
-                throw new SecurityException("Specified package \"" + packageName + "\" under uid "
-                        +  UserHandle.getAppId(uid) + otherUidMessage);
+                String otherUidMessage =
+                            DEBUG ? " but it is really " + nonAppUid : " but it is not";
+                throw new SecurityException("Specified package \"" + packageName
+                            + "\" under uid " +  UserHandle.getAppId(uid) + otherUidMessage);
+            }
+            // We only allow bypassing the attribution tag verification if the proxy is a
+            // system app (or is null), in order to prevent abusive apps clogging the appops
+            // system with unlimited attribution tags via proxy calls.
+            boolean proxyIsSystemAppOrNull = true;
+            if (proxyPackageName != null) {
+                int proxyAppId = UserHandle.getAppId(proxyUid);
+                if (proxyAppId >= Process.FIRST_APPLICATION_UID) {
+                    proxyIsSystemAppOrNull =
+                            mPackageManagerInternal.isSystemPackage(proxyPackageName);
+                }
             }
             return new PackageVerificationResult(RestrictionBypass.UNRESTRICTED,
-                    /* isAttributionTagValid */ true);
+                    /* isAttributionTagValid */ proxyIsSystemAppOrNull);
         }
 
         int userId = UserHandle.getUserId(uid);
         RestrictionBypass bypass = null;
         boolean isAttributionTagValid = false;
 
+        int pkgUid = nonAppUid;
         final long ident = Binder.clearCallingIdentity();
         try {
             PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
@@ -5649,7 +5693,7 @@
             if (nonpackageUid != -1) {
                 packageName = null;
             } else {
-                packageUid = resolveUid(packageName);
+                packageUid = resolveNonAppUid(packageName);
                 if (packageUid < 0) {
                     packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
                             PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
@@ -6749,7 +6793,13 @@
                 if (restricted && attrOp.isRunning()) {
                     attrOp.pause();
                 } else if (attrOp.isPaused()) {
-                    attrOp.resume();
+                    RestrictionBypass bypass = verifyAndGetBypass(uid, ops.packageName, attrOp.tag)
+                            .bypass;
+                    if (!isOpRestrictedLocked(uid, code, ops.packageName, attrOp.tag,
+                            Context.DEVICE_ID_DEFAULT, bypass, false)) {
+                        // Only resume if there are no other restrictions remaining on this op
+                        attrOp.resume();
+                    }
                 }
             }
         }
@@ -7198,7 +7248,7 @@
         }
     }
 
-    private static int resolveUid(String packageName)  {
+    private static int resolveNonAppUid(String packageName)  {
         if (packageName == null) {
             return Process.INVALID_UID;
         }
diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
index 030ce12..fece7a8 100644
--- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
+++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
@@ -65,6 +65,12 @@
                 return setRingerMode();
             case "set-volume":
                 return setVolume();
+            case "get-min-volume":
+                return getMinVolume();
+            case "get-max-volume":
+                return getMaxVolume();
+            case "get-stream-volume":
+                return getStreamVolume();
             case "set-device-volume":
                 return setDeviceVolume();
             case "adj-mute":
@@ -106,6 +112,12 @@
         pw.println("    Sets the Ringer mode to one of NORMAL|SILENT|VIBRATE");
         pw.println("  set-volume STREAM_TYPE VOLUME_INDEX");
         pw.println("    Sets the volume for STREAM_TYPE to VOLUME_INDEX");
+        pw.println("  get-min-volume STREAM_TYPE");
+        pw.println("    Gets the min volume for STREAM_TYPE");
+        pw.println("  get-max-volume STREAM_TYPE");
+        pw.println("    Gets the max volume for STREAM_TYPE");
+        pw.println("  get-stream-volume STREAM_TYPE");
+        pw.println("    Gets the volume for STREAM_TYPE");
         pw.println("  set-device-volume STREAM_TYPE VOLUME_INDEX NATIVE_DEVICE_TYPE");
         pw.println("    Sets for NATIVE_DEVICE_TYPE the STREAM_TYPE volume to VOLUME_INDEX");
         pw.println("  adj-mute STREAM_TYPE");
@@ -296,6 +308,33 @@
         return 0;
     }
 
+    private int getMinVolume() {
+        final Context context = mService.mContext;
+        final AudioManager am = context.getSystemService(AudioManager.class);
+        final int stream = readIntArg();
+        final int result = am.getStreamMinVolume(stream);
+        getOutPrintWriter().println("AudioManager.getStreamMinVolume(" + stream + ") -> " + result);
+        return 0;
+    }
+
+    private int getMaxVolume() {
+        final Context context = mService.mContext;
+        final AudioManager am = context.getSystemService(AudioManager.class);
+        final int stream = readIntArg();
+        final int result = am.getStreamMaxVolume(stream);
+        getOutPrintWriter().println("AudioManager.getStreamMaxVolume(" + stream + ") -> " + result);
+        return 0;
+    }
+
+    private int getStreamVolume() {
+        final Context context = mService.mContext;
+        final AudioManager am = context.getSystemService(AudioManager.class);
+        final int stream = readIntArg();
+        final int result = am.getStreamVolume(stream);
+        getOutPrintWriter().println("AudioManager.getStreamVolume(" + stream + ") -> " + result);
+        return 0;
+    }
+
     private int setDeviceVolume() {
         final Context context = mService.mContext;
         final AudioDeviceVolumeManager advm = (AudioDeviceVolumeManager) context.getSystemService(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 709c13b..f283009 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -47,9 +47,10 @@
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
 import static android.media.AudioManager.STREAM_SYSTEM;
-import static android.media.IAudioManagerNative.HardeningType;
 import static android.media.audio.Flags.autoPublicVolumeApiHardening;
 import static android.media.audio.Flags.automaticBtDeviceType;
+import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
+import static android.media.audio.Flags.cacheGetStreamVolume;
 import static android.media.audio.Flags.concurrentAudioRecordBypassPermission;
 import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency;
 import static android.media.audio.Flags.focusFreezeTestApi;
@@ -836,6 +837,7 @@
 
     private final Executor mAudioServerLifecycleExecutor;
     private long mSysPropListenerNativeHandle;
+    private CacheWatcher mCacheWatcher;
     private final List<Future> mScheduledPermissionTasks = new ArrayList();
 
     private IMediaProjectionManager mProjectionService; // to validate projection token
@@ -898,6 +900,16 @@
         public void permissionUpdateBarrier() {
             AudioService.this.permissionUpdateBarrier();
         }
+
+        /**
+         * Update mute state event for port
+         * @param portId Port id to update
+         * @param event the mute event containing info about the mute
+         */
+        @Override
+        public void portMuteEvent(int portId, int event) {
+            mPlaybackMonitor.portMuteEvent(portId, event, Binder.getCallingUid());
+        }
     };
 
     // List of binder death handlers for setMode() client processes.
@@ -1900,6 +1912,12 @@
             mSpatializerHelper.onRoutingUpdated();
         }
         checkMuteAwaitConnection();
+        if (cacheGetStreamVolume()) {
+            if (DEBUG_VOL) {
+                Log.d(TAG, "Clear volume cache after routing update");
+            }
+            AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API);
+        }
     }
 
     //-----------------------------------------------------------------
@@ -4976,6 +4994,10 @@
                 + ringMyCar());
         pw.println("\tandroid.media.audio.Flags.concurrentAudioRecordBypassPermission:"
                 + concurrentAudioRecordBypassPermission());
+        pw.println("\tandroid.media.audio.Flags.cacheGetStreamMinMaxVolume:"
+                + cacheGetStreamMinMaxVolume());
+        pw.println("\tandroid.media.audio.Flags.cacheGetStreamVolume:"
+                + cacheGetStreamVolume());
     }
 
     private void dumpAudioMode(PrintWriter pw) {
@@ -7042,6 +7064,13 @@
                     streamState.mIsMuted = false;
                 }
             }
+            if (cacheGetStreamVolume()) {
+                if (DEBUG_VOL) {
+                    Log.d(TAG,
+                            "Clear volume cache after possibly changing mute in readAudioSettings");
+                }
+                AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API);
+            }
         }
 
         readVolumeGroupsSettings(userSwitch);
@@ -9262,11 +9291,23 @@
             public void put(int key, int value) {
                 super.put(key, value);
                 record("put", key, value);
+                if (cacheGetStreamVolume()) {
+                    if (DEBUG_VOL) {
+                        Log.d(TAG, "Clear volume cache after update index map");
+                    }
+                    AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API);
+                }
             }
             @Override
             public void setValueAt(int index, int value) {
                 super.setValueAt(index, value);
                 record("setValueAt", keyAt(index), value);
+                if (cacheGetStreamVolume()) {
+                    if (DEBUG_VOL) {
+                        Log.d(TAG, "Clear volume cache after update index map");
+                    }
+                    AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API);
+                }
             }
 
             // Record all changes in the VolumeStreamState
@@ -9366,6 +9407,12 @@
                     mIndexMinNoPerm = mIndexMin;
                 }
             }
+            if (cacheGetStreamMinMaxVolume() && mStreamType == AudioSystem.STREAM_VOICE_CALL) {
+                if (DEBUG_VOL) {
+                    Log.d(TAG, "Clear min volume cache from updateIndexFactors");
+                }
+                AudioManager.clearVolumeCache(AudioManager.VOLUME_MIN_CACHING_API);
+            }
 
             final int status = AudioSystem.initStreamVolume(
                     mStreamType, indexMinVolCurve, indexMaxVolCurve);
@@ -9403,11 +9450,19 @@
          * @param index minimum index expressed in "UI units", i.e. no 10x factor
          */
         public void updateNoPermMinIndex(int index) {
+            boolean changedNoPermMinIndex =
+                    cacheGetStreamMinMaxVolume() && (index * 10) != mIndexMinNoPerm;
             mIndexMinNoPerm = index * 10;
             if (mIndexMinNoPerm < mIndexMin) {
                 Log.e(TAG, "Invalid mIndexMinNoPerm for stream " + mStreamType);
                 mIndexMinNoPerm = mIndexMin;
             }
+            if (changedNoPermMinIndex) {
+                if (DEBUG_VOL) {
+                    Log.d(TAG, "Clear min volume cache from updateNoPermMinIndex");
+                }
+                AudioManager.clearVolumeCache(AudioManager.VOLUME_MIN_CACHING_API);
+            }
         }
 
         /**
@@ -9982,8 +10037,9 @@
          * @return true if the mute state was changed
          */
         public boolean mute(boolean state, boolean apply, String src) {
+            boolean changed;
             synchronized (VolumeStreamState.class) {
-                boolean changed = state != mIsMuted;
+                changed = state != mIsMuted;
                 if (changed) {
                     sMuteLogger.enqueue(
                             new AudioServiceEvents.StreamMuteEvent(mStreamType, state, src));
@@ -10001,8 +10057,16 @@
                         doMute();
                     }
                 }
-                return changed;
             }
+
+            if (cacheGetStreamVolume() && changed) {
+                if (DEBUG_VOL) {
+                    Log.d(TAG, "Clear volume cache after changing mute state");
+                }
+                AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API);
+            }
+
+            return changed;
         }
 
         public void doMute() {
@@ -11030,31 +11094,26 @@
                     }, getAudioPermissionsDelay(), TimeUnit.MILLISECONDS));
                 }
             };
-            mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
-                    PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
-                    task);
+            if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+                mCacheWatcher = new CacheWatcher(task);
+                mCacheWatcher.start();
+            } else {
+                mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
+                        PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
+                        task);
+            }
         } else {
             mAudioSystem.listenForSystemPropertyChange(
                     PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
                     () -> mAudioServerLifecycleExecutor.execute(
                                 mPermissionProvider::onPermissionStateChanged));
         }
-
-        if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
-            new PackageInfoTransducer().start();
-        }
     }
 
     /**
-     * A transducer that converts high-speed changes in the CACHE_KEY_PACKAGE_INFO_CACHE
-     * PropertyInvalidatedCache into low-speed changes in the CACHE_KEY_PACKAGE_INFO_NOTIFY system
-     * property.  This operates on the popcorn principle: changes in the source are done when the
-     * source has been quiet for the soak interval.
-     *
-     * TODO(b/381097912) This is a temporary measure to support migration away from sysprop
-     * sniffing.  It should be cleaned up.
+     * Listens for CACHE_KEY_PACKAGE_INFO_CACHE invalidations to trigger permission syncing
      */
-    private static class PackageInfoTransducer extends Thread {
+    private static class CacheWatcher extends Thread {
 
         // The run/stop signal.
         private final AtomicBoolean mRunning = new AtomicBoolean(false);
@@ -11062,81 +11121,33 @@
         // The source of change information.
         private final PropertyInvalidatedCache.NonceWatcher mWatcher;
 
-        // The handler for scheduling delayed reactions to changes.
-        private final Handler mHandler;
+        // Task to trigger when cache changes
+        private final Runnable mTask;
 
-        // How long to soak changes: 50ms is the legacy choice.
-        private final static long SOAK_TIME_MS = 50;
-
-        // The ubiquitous lock.
-        private final Object mLock = new Object();
-
-        // If positive, this is the soak expiration time.
-        @GuardedBy("mLock")
-        private long mSoakDeadlineMs = -1;
-
-        // A source of unique long values.
-        @GuardedBy("mLock")
-        private long mToken = 0;
-
-        PackageInfoTransducer() {
-            mWatcher = PropertyInvalidatedCache
-                       .getNonceWatcher(PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
-            mHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        PackageInfoTransducer.this.handleMessage(msg);
-                    }};
+        public CacheWatcher(Runnable r) {
+            mWatcher = PropertyInvalidatedCache.getNonceWatcher(
+                    PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
+            mTask = r;
         }
 
         public void run() {
             mRunning.set(true);
             while (mRunning.get()) {
+                doCheck();
                 try {
-                    final int changes = mWatcher.waitForChange();
-                    if (changes == 0 || !mRunning.get()) {
-                        continue;
-                    }
+                    mWatcher.waitForChange();
                 } catch (InterruptedException e) {
+                    Log.wtf(TAG, "Unexpected Interrupt", e);
                     // We don't know why the exception occurred but keep running until told to
                     // stop.
                     continue;
                 }
-                trigger();
             }
         }
 
-        @GuardedBy("mLock")
-        private void updateLocked() {
-            String n = Long.toString(mToken++);
-            SystemPropertySetter.setWithRetry(PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY, n);
-        }
-
-        private void trigger() {
-            synchronized (mLock) {
-                boolean alreadyQueued = mSoakDeadlineMs >= 0;
-                final long nowMs = SystemClock.uptimeMillis();
-                mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
-                if (!alreadyQueued) {
-                    mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
-                    updateLocked();
-                }
-            }
-        }
-
-        private void handleMessage(Message msg) {
-            synchronized (mLock) {
-                if (mSoakDeadlineMs < 0) {
-                    return;  // ???
-                }
-                final long nowMs = SystemClock.uptimeMillis();
-                if (mSoakDeadlineMs > nowMs) {
-                    mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
-                    mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
-                    return;
-                }
-                mSoakDeadlineMs = -1;
-                updateLocked();
+        public synchronized void doCheck() {
+            if (mWatcher.isChanged()) {
+                mTask.run();
             }
         }
 
@@ -14681,6 +14692,7 @@
                 for (AudioMix mix : mMixes) {
                     mix.setVirtualDeviceId(mAttributionSource.getDeviceId());
                 }
+                mAudioSystem.registerPolicyMixes(mMixes, false);
                 return mAudioSystem.registerPolicyMixes(mMixes, true);
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -15312,7 +15324,11 @@
     /** @see AudioManager#permissionUpdateBarrier() */
     public void permissionUpdateBarrier() {
         if (!audioserverPermissions()) return;
-        mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
+        if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+            mCacheWatcher.doCheck();
+        } else {
+            mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
+        }
         List<Future> snapshot;
         synchronized (mScheduledPermissionTasks) {
             snapshot = List.copyOf(mScheduledPermissionTasks);
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index e2e06b6..57b5feb 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -435,6 +435,49 @@
     /**
      * Update event for port
      * @param portId Port id to update
+     * @param event the mute event containing info about the mute
+     * @param binderUid Calling binder uid
+     */
+    public void portMuteEvent(int portId, @PlayerMuteEvent int event, int binderUid) {
+        if (!UserHandle.isCore(binderUid)) {
+            Log.e(TAG, "Forbidden operation from uid " + binderUid);
+            return;
+        }
+
+        synchronized (mPlayerLock) {
+            int piid;
+            if (portToPiidSimplification()) {
+                int idxOfPiid = mPiidToPortId.indexOfValue(portId);
+                if (idxOfPiid < 0) {
+                    Log.w(TAG, "No piid assigned for invalid/internal port id " + portId);
+                    return;
+                }
+                piid = mPiidToPortId.keyAt(idxOfPiid);
+            } else {
+                piid = mPortIdToPiid.get(portId, PLAYER_PIID_INVALID);
+                if (piid == PLAYER_PIID_INVALID) {
+                    Log.w(TAG, "No piid assigned for invalid/internal port id " + portId);
+                    return;
+                }
+            }
+            final AudioPlaybackConfiguration apc = mPlayers.get(piid);
+            if (apc == null) {
+                Log.w(TAG, "No AudioPlaybackConfiguration assigned for piid " + piid);
+                return;
+            }
+
+            if (apc.getPlayerType()
+                    == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+                // FIXME SoundPool not ready for state reporting
+                return;
+            }
+            mEventHandler.sendMessage(
+                mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_MUTED_EVENT, piid, event, null));
+        }
+    }
+   /**
+     * Update event for port
+     * @param portId Port id to update
      * @param event The new port event
      * @param extras The values associated with this event
      * @param binderUid Calling binder uid
@@ -479,15 +522,10 @@
                 return;
             }
 
-            if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED) {
-                mEventHandler.sendMessage(
-                        mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_MUTED_EVENT, piid,
-                                portId,
-                                extras));
-            } else if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_FORMAT) {
+            if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_FORMAT) {
                 mEventHandler.sendMessage(
                         mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_FORMAT, piid,
-                                portId,
+                                -1,
                                 extras));
             }
         }
@@ -1695,9 +1733,7 @@
      * event for player getting muted
      * args:
      *     msg.arg1: piid
-     *     msg.arg2: port id
-     *     msg.obj: extras describing the mute reason
-     *         type: PersistableBundle
+     *     msg.arg2: mute reason
      */
     private static final int MSG_IIL_UPDATE_PLAYER_MUTED_EVENT = 2;
 
@@ -1705,7 +1741,6 @@
      * event for player reporting playback format and spatialization status
      * args:
      *     msg.arg1: piid
-     *     msg.arg2: port id
      *     msg.obj: extras describing the sample rate, channel mask, spatialized
      *         type: PersistableBundle
      */
@@ -1729,17 +1764,9 @@
                         break;
 
                     case MSG_IIL_UPDATE_PLAYER_MUTED_EVENT:
-                        // TODO: replace PersistableBundle with own struct
-                        PersistableBundle extras = (PersistableBundle) msg.obj;
-                        if (extras == null) {
-                            Log.w(TAG, "Received mute event with no extras");
-                            break;
-                        }
-                        @PlayerMuteEvent int eventValue = extras.getInt(EXTRA_PLAYER_EVENT_MUTE);
-
                         synchronized (mPlayerLock) {
                             int piid = msg.arg1;
-
+                            @PlayerMuteEvent int eventValue = msg.arg2;
 
                             int[] eventValues = new int[1];
                             eventValues[0] = eventValue;
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 2d802b2..b6768c9 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -38,7 +38,6 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.ComponentInfoInternal;
-import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.IAuthService;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
 import android.hardware.biometrics.IBiometricService;
@@ -399,12 +398,6 @@
 
             final long identity = Binder.clearCallingIdentity();
             try {
-                // We can't do this above because we need the READ_DEVICE_CONFIG permission, which
-                // the calling user may not possess.
-                if (!Flags.lastAuthenticationTime()) {
-                    throw new UnsupportedOperationException();
-                }
-
                 return mBiometricService.getLastAuthenticationTime(userId, authenticators);
             } finally {
                 Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 290aab2..6eade1c 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -803,6 +803,9 @@
                 case BiometricPrompt.DISMISSED_REASON_USER_CANCEL:
                     error = BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED;
                     break;
+                case BiometricPrompt.DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS:
+                    error = BiometricConstants.BIOMETRIC_ERROR_CONTENT_VIEW_MORE_OPTIONS_BUTTON;
+                    break;
                 default:
             }
 
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 15b1f22..a5058dd 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -44,7 +44,6 @@
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricStateListener;
-import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
 import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -906,10 +905,6 @@
                 int userId, @Authenticators.Types int authenticators) {
             super.getLastAuthenticationTime_enforcePermission();
 
-            if (!Flags.lastAuthenticationTime()) {
-                throw new UnsupportedOperationException();
-            }
-
             Slogf.d(TAG, "getLastAuthenticationTime(userId=%d, authenticators=0x%x)",
                     userId, authenticators);
 
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index d435144..373287d 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -21,6 +21,7 @@
 import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.window.DesktopExperienceFlags;
 
 import com.android.server.display.feature.flags.Flags;
 import com.android.server.display.utils.DebugUtils;
@@ -244,13 +245,19 @@
             Flags.FLAG_ENABLE_PLUGIN_MANAGER,
             Flags::enablePluginManager
     );
+
+    private final FlagState mEnableHdrOverridePluginTypeFlagState = new FlagState(
+            Flags.FLAG_ENABLE_HDR_OVERRIDE_PLUGIN_TYPE,
+            Flags::enableHdrOverridePluginType
+    );
+
     private final FlagState mDisplayListenerPerformanceImprovementsFlagState = new FlagState(
             Flags.FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS,
             Flags::displayListenerPerformanceImprovements
     );
     private final FlagState mEnableDisplayContentModeManagementFlagState = new FlagState(
             Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
-            Flags::enableDisplayContentModeManagement
+            DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT::isTrue
     );
 
     private final FlagState mSubscribeGranularDisplayEvents = new FlagState(
@@ -549,6 +556,10 @@
         return mEnablePluginManagerFlagState.isEnabled();
     }
 
+    public boolean isHdrOverrideEnabled() {
+        return mEnableHdrOverridePluginTypeFlagState.isEnabled();
+    }
+
     /**
      * @return {@code true} if the flag for display listener performance improvements is enabled
      */
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 63cd2d7..7890db1 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -454,6 +454,14 @@
 }
 
 flag {
+    name: "enable_hdr_override_plugin_type"
+    namespace: "display_manager"
+    description: "Enable hdr override plugin type"
+    bug: "389873155"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "enable_display_content_mode_management"
     namespace: "lse_desktop_experience"
     description: "Enable switching the content mode of connected displays between mirroring and extened. Also change the default content mode to extended mode."
@@ -501,3 +509,11 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "display_category_built_in"
+    namespace: "display_manager"
+    description: "Add a new category to get the built in displays."
+    bug: "293651324"
+    is_fixed_read_only: false
+}
diff --git a/services/core/java/com/android/server/display/plugin/PluginManager.java b/services/core/java/com/android/server/display/plugin/PluginManager.java
index d409997..a8c4e7e 100644
--- a/services/core/java/com/android/server/display/plugin/PluginManager.java
+++ b/services/core/java/com/android/server/display/plugin/PluginManager.java
@@ -30,7 +30,9 @@
 import java.io.PrintWriter;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Responsible for loading Plugins. Plugins and PluginSupplier are loaded from
@@ -43,7 +45,6 @@
             "com.android.server.display.plugin.PluginsProviderImpl";
     private static final String TAG = "PluginManager";
 
-    private final DisplayManagerFlags mFlags;
     private final PluginStorage mPluginStorage;
     private final List<Plugin> mPlugins;
 
@@ -53,10 +54,11 @@
 
     @VisibleForTesting
     PluginManager(Context context, DisplayManagerFlags flags, Injector injector) {
-        mFlags = flags;
-        mPluginStorage = injector.getPluginStorage();
-        if (mFlags.isPluginManagerEnabled()) {
-            mPlugins = Collections.unmodifiableList(injector.loadPlugins(context, mPluginStorage));
+        Set<PluginType<?>> enabledTypes = injector.getEnabledPluginTypes(flags);
+        mPluginStorage = injector.getPluginStorage(enabledTypes);
+        if (flags.isPluginManagerEnabled()) {
+            mPlugins = Collections.unmodifiableList(injector.loadPlugins(
+                    context, mPluginStorage, enabledTypes));
             Slog.d(TAG, "loaded Plugins:" + mPlugins);
         } else {
             mPlugins = List.of();
@@ -74,15 +76,17 @@
     /**
      * Adds change listener for particular plugin type
      */
-    public <T> void subscribe(PluginType<T> type, PluginChangeListener<T> listener) {
-        mPluginStorage.addListener(type, listener);
+    public <T> void subscribe(PluginType<T> type, String uniqueDisplayId,
+            PluginChangeListener<T> listener) {
+        mPluginStorage.addListener(type, uniqueDisplayId, listener);
     }
 
     /**
      * Removes change listener
      */
-    public <T> void unsubscribe(PluginType<T> type, PluginChangeListener<T> listener) {
-        mPluginStorage.removeListener(type, listener);
+    public <T> void unsubscribe(PluginType<T> type, String uniqueDisplayId,
+            PluginChangeListener<T> listener) {
+        mPluginStorage.removeListener(type, uniqueDisplayId, listener);
     }
 
     /**
@@ -108,11 +112,21 @@
     }
 
     static class Injector {
-        PluginStorage getPluginStorage() {
-            return new PluginStorage();
+
+        Set<PluginType<?>> getEnabledPluginTypes(DisplayManagerFlags flags) {
+            Set<PluginType<?>> enabledTypes = new HashSet<>();
+            if (flags.isHdrOverrideEnabled()) {
+                enabledTypes.add(PluginType.HDR_BOOST_OVERRIDE);
+            }
+            return enabledTypes;
         }
 
-        List<Plugin> loadPlugins(Context context, PluginStorage storage) {
+        PluginStorage getPluginStorage(Set<PluginType<?>> enabledTypes) {
+            return new PluginStorage(enabledTypes);
+        }
+
+        List<Plugin> loadPlugins(Context context, PluginStorage storage,
+                Set<PluginType<?>> enabledTypes) {
             String providerJarPath = context
                     .getString(com.android.internal.R.string.config_pluginsProviderJarPath);
             Slog.d(TAG, "loading plugins from:" + providerJarPath);
@@ -127,7 +141,7 @@
                 Class<? extends PluginsProvider> cp = pathClassLoader.loadClass(PROVIDER_IMPL_CLASS)
                         .asSubclass(PluginsProvider.class);
                 PluginsProvider provider = cp.getDeclaredConstructor().newInstance();
-                return provider.getPlugins(context, storage);
+                return provider.getPlugins(context, storage, enabledTypes);
             } catch (ClassNotFoundException e) {
                 Slog.e(TAG, "loading failed: " + PROVIDER_IMPL_CLASS + " is not found in"
                         + providerJarPath, e);
diff --git a/services/core/java/com/android/server/display/plugin/PluginStorage.java b/services/core/java/com/android/server/display/plugin/PluginStorage.java
index dd3415f..d17fbe2 100644
--- a/services/core/java/com/android/server/display/plugin/PluginStorage.java
+++ b/services/core/java/com/android/server/display/plugin/PluginStorage.java
@@ -20,10 +20,14 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.tools.r8.keepanno.annotations.KeepForApi;
 
 import java.io.PrintWriter;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -35,42 +39,111 @@
 public class PluginStorage {
     private static final String TAG = "PluginStorage";
 
+    // Special ID used to indicate that given value is to be applied globally, rather than to a
+    // specific display. If both GLOBAL and specific display values are present - specific display
+    // value is selected.
+    @VisibleForTesting
+    static final String GLOBAL_ID = "GLOBAL";
+
     private final Object mLock = new Object();
     @GuardedBy("mLock")
-    private final Map<PluginType<?>, Object> mValues = new HashMap<>();
+    private final Map<PluginType<?>, ValuesContainer<?>> mValues = new HashMap<>();
     @GuardedBy("mLock")
     private final Map<PluginType<?>, ListenersContainer<?>> mListeners = new HashMap<>();
     @GuardedBy("mLock")
-    private final PluginEventStorage mPluginEventStorage = new PluginEventStorage();
+    private final Map<String, PluginEventStorage> mPluginEventStorages = new HashMap<>();
 
     /**
-     * Updates value in storage and forwards it to corresponding listeners.
-     * Should be called by OEM Plugin implementation in order to provide communicate with Framework
+     * Updates value in storage and forwards it to corresponding listeners for all displays
+     * that does not have display specific value.
+     * Should be called by OEM Plugin implementation in order to communicate with Framework
      */
     @KeepForApi
-    public <T> void updateValue(PluginType<T> type, @Nullable T value) {
-        Slog.d(TAG, "updateValue, type=" + type.mName + "; value=" + value);
+    public <T> void updateGlobalValue(PluginType<T> type, @Nullable T value) {
+        updateValue(type, GLOBAL_ID, value);
+    }
+
+    private final Set<PluginType<?>> mEnabledTypes;
+
+    PluginStorage(Set<PluginType<?>> enabledTypes) {
+        mEnabledTypes = Collections.unmodifiableSet(enabledTypes);
+    }
+
+    /**
+     * Updates value in storage and forwards it to corresponding listeners for specific display.
+     * Should be called by OEM Plugin implementation in order to communicate with Framework
+     * @param type - plugin type, that need to be updated
+     * @param uniqueDisplayId - uniqueDisplayId that this type/value should be applied to
+     * @param value - plugin value for particular type and display
+     */
+    @KeepForApi
+    public <T> void updateValue(PluginType<T> type, String uniqueDisplayId, @Nullable T value) {
+        if (isTypeDisabled(type)) {
+            Slog.d(TAG, "updateValue ignored for disabled type=" + type.mName);
+            return;
+        }
+        Slog.d(TAG, "updateValue, type=" + type.mName + "; value=" + value
+                + "; displayId=" + uniqueDisplayId);
         Set<PluginManager.PluginChangeListener<T>> localListeners;
+        T valueToNotify;
         synchronized (mLock) {
-            mValues.put(type, value);
-            mPluginEventStorage.onValueUpdated(type);
-            ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
-            localListeners = new LinkedHashSet<>(container.mListeners);
+            ValuesContainer<T> valuesByType = getValuesContainerLocked(type);
+            valuesByType.updateValueLocked(uniqueDisplayId, value);
+            // if value was set to null, we might need to notify with GLOBAL value instead
+            valueToNotify = valuesByType.getValueLocked(uniqueDisplayId);
+
+            PluginEventStorage storage = mPluginEventStorages.computeIfAbsent(uniqueDisplayId,
+                    d -> new PluginEventStorage());
+            storage.onValueUpdated(type);
+
+            localListeners =  getListenersForUpdateLocked(type, uniqueDisplayId);
         }
         Slog.d(TAG, "updateValue, notifying listeners=" + localListeners);
-        localListeners.forEach(l -> l.onChanged(value));
+        localListeners.forEach(l -> l.onChanged(valueToNotify));
+    }
+
+    @GuardedBy("mLock")
+    private <T> Set<PluginManager.PluginChangeListener<T>> getListenersForUpdateLocked(
+            PluginType<T> type, String uniqueDisplayId) {
+        ListenersContainer<T> listenersContainer = getListenersContainerLocked(type);
+        Set<PluginManager.PluginChangeListener<T>> localListeners = new LinkedHashSet<>();
+        // if GLOBAL value change we need to notify only listeners for displays that does not
+        // have display specific value
+        if (GLOBAL_ID.equals(uniqueDisplayId)) {
+            ValuesContainer<T> valuesContainer = getValuesContainerLocked(type);
+            Set<String> excludedDisplayIds = valuesContainer.getNonGlobalDisplaysLocked();
+            listenersContainer.mListeners.forEach((localDisplayId, listeners) -> {
+                if (!excludedDisplayIds.contains(localDisplayId)) {
+                    localListeners.addAll(listeners);
+                }
+            });
+        } else {
+            localListeners.addAll(
+                    listenersContainer.mListeners.getOrDefault(uniqueDisplayId, Set.of()));
+        }
+        return localListeners;
     }
 
     /**
      * Adds listener for PluginType. If storage already has value for this type, listener will
      * be notified immediately.
      */
-    <T> void addListener(PluginType<T> type, PluginManager.PluginChangeListener<T> listener) {
+    <T> void addListener(PluginType<T> type, String uniqueDisplayId,
+            PluginManager.PluginChangeListener<T> listener) {
+        if (isTypeDisabled(type)) {
+            Slog.d(TAG, "addListener ignored for disabled type=" + type.mName);
+            return;
+        }
+        if (GLOBAL_ID.equals(uniqueDisplayId)) {
+            Slog.d(TAG, "addListener ignored for GLOBAL_ID, type=" + type.mName);
+            return;
+        }
         T value = null;
         synchronized (mLock) {
-            ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
-            if (container.mListeners.add(listener)) {
-                value = getValueForTypeLocked(type);
+            ListenersContainer<T> container = getListenersContainerLocked(type);
+            if (container.addListenerLocked(uniqueDisplayId, listener)) {
+                ValuesContainer<T> valuesContainer = getValuesContainerLocked(type);
+                value = valuesContainer.getValueLocked(uniqueDisplayId);
             }
         }
         if (value != null) {
@@ -81,10 +154,19 @@
     /**
      * Removes listener
      */
-    <T> void removeListener(PluginType<T> type, PluginManager.PluginChangeListener<T> listener) {
+    <T> void removeListener(PluginType<T> type, String uniqueDisplayId,
+            PluginManager.PluginChangeListener<T> listener) {
+        if (isTypeDisabled(type)) {
+            Slog.d(TAG, "removeListener ignored for disabled type=" + type.mName);
+            return;
+        }
+        if (GLOBAL_ID.equals(uniqueDisplayId)) {
+            Slog.d(TAG, "removeListener ignored for GLOBAL_ID, type=" + type.mName);
+            return;
+        }
         synchronized (mLock) {
-            ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
-            container.mListeners.remove(listener);
+            ListenersContainer<T> container = getListenersContainerLocked(type);
+            container.removeListenerLocked(uniqueDisplayId, listener);
         }
     }
 
@@ -92,42 +174,41 @@
      * Print the object's state and debug information into the given stream.
      */
     void dump(PrintWriter pw) {
-        Map<PluginType<?>, Object> localValues;
+        Map<PluginType<?>, Map<String, Object>> localValues = new HashMap<>();
         @SuppressWarnings("rawtypes")
-        Map<PluginType, Set> localListeners = new HashMap<>();
-        List<PluginEventStorage.TimeFrame> timeFrames;
+        Map<PluginType, Map<String, Set>> localListeners = new HashMap<>();
+        Map<String, List<PluginEventStorage.TimeFrame>> timeFrames = new HashMap<>();
         synchronized (mLock) {
-            timeFrames = mPluginEventStorage.getTimeFrames();
-            localValues = new HashMap<>(mValues);
-            mListeners.forEach((type, container) -> localListeners.put(type, container.mListeners));
+            mPluginEventStorages.forEach((displayId, storage) -> {
+                timeFrames.put(displayId, storage.getTimeFrames());
+            });
+            mValues.forEach((type, valueContainer) -> {
+                localValues.put(type, new HashMap<>(valueContainer.mValues));
+            });
+            mListeners.forEach((type, container) -> {
+                localListeners.put(type, new HashMap<>(container.mListeners));
+            });
         }
         pw.println("PluginStorage:");
         pw.println("values=" + localValues);
         pw.println("listeners=" + localListeners);
         pw.println("PluginEventStorage:");
-        for (PluginEventStorage.TimeFrame timeFrame: timeFrames) {
-            timeFrame.dump(pw);
+        for (Map.Entry<String, List<PluginEventStorage.TimeFrame>> timeFrameEntry :
+                timeFrames.entrySet()) {
+            pw.println("TimeFrames for displayId=" + timeFrameEntry.getKey());
+            for (PluginEventStorage.TimeFrame timeFrame : timeFrameEntry.getValue()) {
+                timeFrame.dump(pw);
+            }
         }
     }
 
+    private boolean isTypeDisabled(PluginType<?> type) {
+        return !mEnabledTypes.contains(type);
+    }
+
     @GuardedBy("mLock")
     @SuppressWarnings("unchecked")
-    private <T> T getValueForTypeLocked(PluginType<T> type) {
-        Object value = mValues.get(type);
-        if (value == null) {
-            return null;
-        } else if (type.mType == value.getClass()) {
-            return (T) value;
-        } else {
-            Slog.d(TAG, "getValueForType: unexpected value type=" + value.getClass().getName()
-                    + ", expected=" + type.mType.getName());
-            return null;
-        }
-    }
-
-    @GuardedBy("mLock")
-    @SuppressWarnings("unchecked")
-    private <T> ListenersContainer<T> getListenersContainerForTypeLocked(PluginType<T> type) {
+    private <T> ListenersContainer<T> getListenersContainerLocked(PluginType<T> type) {
         ListenersContainer<?> container = mListeners.get(type);
         if (container == null) {
             ListenersContainer<T> lc = new ListenersContainer<>();
@@ -138,7 +219,65 @@
         }
     }
 
+    @GuardedBy("mLock")
+    @SuppressWarnings("unchecked")
+    private <T> ValuesContainer<T> getValuesContainerLocked(PluginType<T> type) {
+        ValuesContainer<?> container = mValues.get(type);
+        if (container == null) {
+            ValuesContainer<T> vc = new ValuesContainer<>();
+            mValues.put(type, vc);
+            return vc;
+        } else {
+            return (ValuesContainer<T>) container;
+        }
+    }
+
     private static final class ListenersContainer<T> {
-        private final Set<PluginManager.PluginChangeListener<T>> mListeners = new LinkedHashSet<>();
+        private final Map<String, Set<PluginManager.PluginChangeListener<T>>> mListeners =
+                new LinkedHashMap<>();
+
+        private boolean addListenerLocked(
+                String uniqueDisplayId, PluginManager.PluginChangeListener<T> listener) {
+            Set<PluginManager.PluginChangeListener<T>> listenersForDisplay =
+                    mListeners.computeIfAbsent(uniqueDisplayId, k -> new LinkedHashSet<>());
+            return listenersForDisplay.add(listener);
+        }
+
+        private void removeListenerLocked(String uniqueDisplayId,
+                PluginManager.PluginChangeListener<T> listener) {
+            Set<PluginManager.PluginChangeListener<T>> listenersForDisplay = mListeners.get(
+                    uniqueDisplayId);
+            if (listenersForDisplay == null) {
+                return;
+            }
+
+            listenersForDisplay.remove(listener);
+
+            if (listenersForDisplay.isEmpty()) {
+                mListeners.remove(uniqueDisplayId);
+            }
+        }
+    }
+
+    private static final class ValuesContainer<T> {
+        private final Map<String, T> mValues = new HashMap<>();
+
+        private void updateValueLocked(String uniqueDisplayId, @Nullable T value) {
+            if (value == null) {
+                mValues.remove(uniqueDisplayId);
+            } else {
+                mValues.put(uniqueDisplayId, value);
+            }
+        }
+
+        private Set<String> getNonGlobalDisplaysLocked() {
+            Set<String> keys = new HashSet<>(mValues.keySet());
+            keys.remove(GLOBAL_ID);
+            return keys;
+        }
+
+        private @Nullable T getValueLocked(String displayId) {
+            return mValues.getOrDefault(displayId, mValues.get(GLOBAL_ID));
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/plugin/PluginType.java b/services/core/java/com/android/server/display/plugin/PluginType.java
index fb60833..e4d2f85 100644
--- a/services/core/java/com/android/server/display/plugin/PluginType.java
+++ b/services/core/java/com/android/server/display/plugin/PluginType.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.annotations.Keep;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.plugin.types.HdrBoostOverride;
 
 /**
  * Represent customisation entry point to Framework. OEM and Framework team should define
@@ -28,6 +29,15 @@
  */
 @Keep
 public class PluginType<T> {
+    /*
+    * PluginType for HDR boost override. If set, system will use overridden value instead
+    * system default parameters. To switch back to default system behaviour, Plugin should set
+    * this type value to null.
+    * Value change will trigger whole power state recalculation, so plugins should not update
+    * value for this type too often.
+    */
+    public static final PluginType<HdrBoostOverride> HDR_BOOST_OVERRIDE = new PluginType<>(
+            HdrBoostOverride.class, "hdr_boost_override");
 
     final Class<T> mType;
     final String mName;
diff --git a/services/core/java/com/android/server/display/plugin/PluginsProvider.java b/services/core/java/com/android/server/display/plugin/PluginsProvider.java
index 9ad85f6..ec74c86 100644
--- a/services/core/java/com/android/server/display/plugin/PluginsProvider.java
+++ b/services/core/java/com/android/server/display/plugin/PluginsProvider.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.keepanno.annotations.KeepForApi;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Interface that OEMs should implement in order to supply Plugins to PluginManager
@@ -32,5 +33,6 @@
      * Provides list of Plugins to PluginManager
      */
     @NonNull
-    List<Plugin> getPlugins(Context context, PluginStorage storage);
+    List<Plugin> getPlugins(
+            Context context, PluginStorage storage, Set<PluginType<?>> enabledTypes);
 }
diff --git a/services/core/java/com/android/server/display/plugin/types/HdrBoostOverride.java b/services/core/java/com/android/server/display/plugin/types/HdrBoostOverride.java
new file mode 100644
index 0000000..22e024d
--- /dev/null
+++ b/services/core/java/com/android/server/display/plugin/types/HdrBoostOverride.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2025 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.plugin.types;
+
+import android.annotation.FloatRange;
+import android.os.PowerManager;
+
+import com.android.server.display.DisplayBrightnessState;
+
+/**
+ * HDR boost override value.
+ * @param sdrHdrRatio - HDR to SDR multiplier, if < 1 HDR boost is off.
+ * @param maxHdrBrightness - Brightness max when boosted. Value in range from BRIGHTNESS_MIN to
+ *                         BRIGHTNESS_MAX. If not used should be set to PowerManager.BRIGHTNESS_MAX
+ * @param customTransitionRate - Custom transition rate for transitioning to new HDR brightness.
+ *                             If not used should be set to
+ *                             DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET
+ */
+public record HdrBoostOverride(
+        @FloatRange(from = 0)
+        float sdrHdrRatio,
+        @FloatRange(from = PowerManager.BRIGHTNESS_MIN, to = PowerManager.BRIGHTNESS_MAX)
+        float maxHdrBrightness,
+        float customTransitionRate) {
+    /**
+     * Constant for HDR boost off. Plugins should use this constant instead of creating new objects
+     */
+    private static final HdrBoostOverride HDR_OFF = new HdrBoostOverride(0,
+            PowerManager.BRIGHTNESS_MAX, DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET);
+
+    /**
+     * Create HdrBoostOverride for HDR boost off
+     */
+    public static HdrBoostOverride forHdrOff() {
+        return HDR_OFF;
+    }
+
+    /**
+     * Create HdrBoostOverride for sdr-hdr ration override
+     */
+    public static HdrBoostOverride forSdrHdrRatio(float sdrHdrRatio) {
+        return new HdrBoostOverride(sdrHdrRatio,
+                PowerManager.BRIGHTNESS_MAX, DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET);
+    }
+}
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index e28939b..6e5e0fd5 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.UserInfo;
+import android.credentials.flags.Flags;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
@@ -1173,8 +1174,19 @@
                     final String[] serviceNames = mServiceNameResolver.getDefaultServiceNameList(
                             userId);
                     if (serviceNames != null) {
+                        if (Flags.packageUpdateFixEnabled()) {
+                            if (mServiceNameResolver.isConfiguredInMultipleMode()) {
+                                // Remove any service that is in the cache but is no longer valid
+                                // after this modification for this particular package
+                                removeInvalidCachedServicesLocked(serviceNames, packageName,
+                                        userId);
+                            }
+                        }
+
+                        // Update services that are still valid
                         for (int i = 0; i < serviceNames.length; i++) {
-                            peekAndUpdateCachedServiceLocked(packageName, userId, serviceNames[i]);
+                            peekAndUpdateCachedServiceLocked(packageName, userId,
+                                    serviceNames[i]);
                         }
                     }
                 }
@@ -1231,6 +1243,36 @@
     }
 
     @GuardedBy("mLock")
+    @SuppressWarnings("GuardedBy") // ErrorProne requires this.mLock for
+    // handleServiceRemovedMultiModeLocked which is the same
+    private void removeInvalidCachedServicesLocked(String[] validServices,
+            String packageName, int userId) {
+        visitServicesLocked((s) -> {
+            ComponentName serviceComponentName = s
+                    .getServiceComponentName();
+            if (serviceComponentName != null && serviceComponentName
+                    .getPackageName().equals(packageName)) {
+                if (!serviceInValidServiceList(serviceComponentName,
+                        validServices)) {
+                    handleServiceRemovedMultiModeLocked(
+                            serviceComponentName, userId);
+                }
+            }
+        });
+    }
+
+    private boolean serviceInValidServiceList(ComponentName serviceComponentName,
+            String[] serviceNames) {
+        for (String service: serviceNames) {
+            ComponentName componentName = ComponentName.unflattenFromString(service);
+            if (componentName != null && componentName.equals(serviceComponentName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @GuardedBy("mLock")
     @SuppressWarnings("unused")
     protected void onServicePackageDataClearedMultiModeLocked(String packageName, int userId) {
         if (verbose) {
@@ -1246,6 +1288,12 @@
     }
 
     @GuardedBy("mLock")
+    protected void handleServiceRemovedMultiModeLocked(ComponentName serviceComponentName,
+            int userId) {
+        if (verbose) Slog.v(mTag, "handleServiceRemovedMultiModeLocked(" + userId + ")");
+    }
+
+    @GuardedBy("mLock")
     protected void removeServiceFromCache(@NonNull S service, int userId) {
         if (mServicesCacheList.get(userId) != null) {
             mServicesCacheList.get(userId).remove(service);
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index af025c0..81d3e0c 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -323,7 +323,7 @@
      * if the service is disabled.
      */
     @Nullable
-    public final ComponentName getServiceComponentName() {
+    public ComponentName getServiceComponentName() {
         synchronized (mLock) {
             return mServiceInfo == null ? null : mServiceInfo.getComponentName();
         }
diff --git a/services/core/java/com/android/server/infra/OWNERS b/services/core/java/com/android/server/infra/OWNERS
index 4fea05d..0f0d382 100644
--- a/services/core/java/com/android/server/infra/OWNERS
+++ b/services/core/java/com/android/server/infra/OWNERS
@@ -1,3 +1,4 @@
 # Bug component: 655446
 
 srazdan@google.com
+reemabajwa@google.com
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index fd755e3..108afba 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -23,7 +23,6 @@
 import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
 import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
 import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut;
-import static com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -37,6 +36,7 @@
 import android.util.IndentingPrintWriter;
 import android.util.SparseArray;
 import android.view.KeyEvent;
+import android.window.DesktopModeFlags;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -152,7 +152,7 @@
                 ),
                 createKeyGesture(
                         KeyEvent.KEYCODE_S,
-                        KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+                        KeyEvent.META_META_ON,
                         KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
                 ),
                 createKeyGesture(
@@ -186,21 +186,11 @@
                         KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
                 ),
                 createKeyGesture(
-                        KeyEvent.KEYCODE_DPAD_LEFT,
-                        KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
-                ),
-                createKeyGesture(
                         KeyEvent.KEYCODE_DPAD_RIGHT,
                         KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
                         KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
                 ),
                 createKeyGesture(
-                        KeyEvent.KEYCODE_DPAD_RIGHT,
-                        KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT
-                ),
-                createKeyGesture(
                         KeyEvent.KEYCODE_SLASH,
                         KeyEvent.META_META_ON,
                         KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER
@@ -243,7 +233,7 @@
                             KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS));
         }
-        if (enableTaskResizingKeyboardShortcuts()) {
+        if (DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue()) {
             systemShortcuts.add(createKeyGesture(
                     KeyEvent.KEYCODE_LEFT_BRACKET,
                     KeyEvent.META_META_ON,
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 4e5c720..d2486fe 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.graphics.PointF;
-import android.hardware.display.DisplayTopology;
+import android.hardware.display.DisplayTopologyGraph;
 import android.hardware.display.DisplayViewport;
 import android.hardware.input.KeyGestureEvent;
 import android.os.IBinder;
@@ -51,7 +51,7 @@
      * Called by {@link com.android.server.display.DisplayManagerService} to inform InputManager
      * about changes in the displays topology.
      */
-    public abstract void setDisplayTopology(DisplayTopology topology);
+    public abstract void setDisplayTopology(DisplayTopologyGraph topology);
 
     /**
      * Called by the power manager to tell the input manager whether it should start
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 2ba35d6..af021e5 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -52,7 +52,7 @@
 import android.hardware.SensorPrivacyManager.Sensors;
 import android.hardware.SensorPrivacyManagerInternal;
 import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.DisplayTopology;
+import android.hardware.display.DisplayTopologyGraph;
 import android.hardware.display.DisplayViewport;
 import android.hardware.input.AidlInputGestureData;
 import android.hardware.input.HostUsiVersion;
@@ -662,8 +662,8 @@
         mNative.setPointerDisplayId(mWindowManagerCallbacks.getPointerDisplayId());
     }
 
-    private void setDisplayTopologyInternal(DisplayTopology topology) {
-        mNative.setDisplayTopology(topology.getGraph());
+    private void setDisplayTopologyInternal(DisplayTopologyGraph topology) {
+        mNative.setDisplayTopology(topology);
     }
 
     /**
@@ -1261,6 +1261,15 @@
 
     @EnforcePermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
     @Override // Binder call
+    public void setKeyboardLayoutOverrideForInputDevice(InputDeviceIdentifier identifier,
+            String keyboardLayoutDescriptor) {
+        super.setKeyboardLayoutOverrideForInputDevice_enforcePermission();
+        mKeyboardLayoutManager.setKeyboardLayoutOverrideForInputDevice(identifier,
+                keyboardLayoutDescriptor);
+    }
+
+    @EnforcePermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
+    @Override // Binder call
     public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
             @UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
             @Nullable InputMethodSubtype imeSubtype, String keyboardLayoutDescriptor) {
@@ -3524,7 +3533,7 @@
         }
 
         @Override
-        public void setDisplayTopology(DisplayTopology topology) {
+        public void setDisplayTopology(DisplayTopologyGraph topology) {
             setDisplayTopologyInternal(topology);
         }
 
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 1d1a178..2f22853 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -22,8 +22,6 @@
 import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
 import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT;
 
-import static com.android.hardware.input.Flags.keyboardLayoutManagerMultiUserImeSetup;
-
 import android.annotation.AnyThread;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
@@ -68,7 +66,6 @@
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.R;
@@ -113,6 +110,7 @@
     private static final int MSG_UPDATE_EXISTING_DEVICES = 1;
     private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 2;
     private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 3;
+    private static final String GLOBAL_OVERRIDE_KEY = "GLOBAL_OVERRIDE_KEY";
 
     private final Context mContext;
     private final NativeInputManagerService mNative;
@@ -508,26 +506,44 @@
     }
 
     @AnyThread
-    public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
-            @UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
-            @Nullable InputMethodSubtype imeSubtype,
+    public void setKeyboardLayoutOverrideForInputDevice(InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor) {
-        Objects.requireNonNull(keyboardLayoutDescriptor,
-                "keyboardLayoutDescriptor must not be null");
         InputDevice inputDevice = getInputDevice(identifier);
         if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
             return;
         }
         KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice);
-        String layoutKey = new LayoutKey(keyboardIdentifier,
+        setKeyboardLayoutForInputDeviceInternal(keyboardIdentifier, GLOBAL_OVERRIDE_KEY,
+                keyboardLayoutDescriptor);
+    }
+
+    @AnyThread
+    public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
+            @UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
+            @Nullable InputMethodSubtype imeSubtype,
+            String keyboardLayoutDescriptor) {
+        InputDevice inputDevice = getInputDevice(identifier);
+        if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
+            return;
+        }
+        KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice);
+        final String datastoreKey = new LayoutKey(keyboardIdentifier,
                 new ImeInfo(userId, imeInfo, imeSubtype)).toString();
+        setKeyboardLayoutForInputDeviceInternal(keyboardIdentifier, datastoreKey,
+                keyboardLayoutDescriptor);
+    }
+
+    private void setKeyboardLayoutForInputDeviceInternal(KeyboardIdentifier identifier,
+            String datastoreKey, String keyboardLayoutDescriptor) {
+        Objects.requireNonNull(keyboardLayoutDescriptor,
+                "keyboardLayoutDescriptor must not be null");
         synchronized (mDataStore) {
             try {
-                if (mDataStore.setKeyboardLayout(keyboardIdentifier.toString(), layoutKey,
+                if (mDataStore.setKeyboardLayout(identifier.toString(), datastoreKey,
                         keyboardLayoutDescriptor)) {
                     if (DEBUG) {
                         Slog.d(TAG, "setKeyboardLayoutForInputDevice() " + identifier
-                                + " key: " + layoutKey
+                                + " key: " + datastoreKey
                                 + " keyboardLayoutDescriptor: " + keyboardLayoutDescriptor);
                     }
                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
@@ -635,6 +651,12 @@
             if (layout != null) {
                 return new KeyboardLayoutSelectionResult(layout, LAYOUT_SELECTION_CRITERIA_USER);
             }
+
+            layout = mDataStore.getKeyboardLayout(keyboardIdentifier.toString(),
+                    GLOBAL_OVERRIDE_KEY);
+            if (layout != null) {
+                return new KeyboardLayoutSelectionResult(layout, LAYOUT_SELECTION_CRITERIA_DEVICE);
+            }
         }
 
         synchronized (mKeyboardLayoutCache) {
@@ -1056,8 +1078,6 @@
         List<ImeInfo> imeInfoList = new ArrayList<>();
         UserManager userManager = Objects.requireNonNull(
                 mContext.getSystemService(UserManager.class));
-        InputMethodManager inputMethodManager = Objects.requireNonNull(
-                mContext.getSystemService(InputMethodManager.class));
         // Need to use InputMethodManagerInternal to call getEnabledInputMethodListAsUser()
         // instead of using InputMethodManager which uses enforceCallingPermissions() that
         // breaks when we are calling the method for work profile user ID since it doesn't check
@@ -1068,14 +1088,10 @@
             for (InputMethodInfo imeInfo :
                     inputMethodManagerInternal.getEnabledInputMethodListAsUser(
                             userId)) {
-                final List<InputMethodSubtype> imeSubtypes;
-                if (keyboardLayoutManagerMultiUserImeSetup()) {
-                    imeSubtypes = inputMethodManagerInternal.getEnabledInputMethodSubtypeListAsUser(
-                            imeInfo.getId(), true /* allowsImplicitlyEnabledSubtypes */, userId);
-                } else {
-                    imeSubtypes = inputMethodManager.getEnabledInputMethodSubtypeList(imeInfo,
-                            true /* allowsImplicitlyEnabledSubtypes */);
-                }
+                final List<InputMethodSubtype> imeSubtypes =
+                        inputMethodManagerInternal.getEnabledInputMethodSubtypeListAsUser(
+                                imeInfo.getId(), true /* allowsImplicitlyEnabledSubtypes */,
+                                userId);
                 for (InputMethodSubtype imeSubtype : imeSubtypes) {
                     if (!imeSubtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
                         continue;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index b9352ed..ddace17 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -42,6 +42,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.util.Collection;
+import java.util.Optional;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -372,10 +373,12 @@
 
     /* package */ void onEndpointSessionOpenRequest(
             int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
-        boolean success =
+        Optional<Byte> error =
                 onEndpointSessionOpenRequestInternal(sessionId, initiator, serviceDescriptor);
-        if (!success) {
-            cleanupSessionResources(sessionId);
+        if (error.isPresent()) {
+            halCloseEndpointSessionNoThrow(sessionId, error.get());
+            onCloseEndpointSession(sessionId, error.get());
+            // Resource cleanup is done in onCloseEndpointSession
         }
     }
 
@@ -422,7 +425,7 @@
         }
     }
 
-    private boolean onEndpointSessionOpenRequestInternal(
+    private Optional<Byte> onEndpointSessionOpenRequestInternal(
             int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
         if (!hasEndpointPermissions(initiator)) {
             Log.e(
@@ -431,22 +434,21 @@
                             + initiator
                             + " doesn't have permission for "
                             + mEndpointInfo);
-            halCloseEndpointSessionNoThrow(sessionId, Reason.PERMISSION_DENIED);
-            return false;
+            return Optional.of(Reason.PERMISSION_DENIED);
         }
 
         synchronized (mOpenSessionLock) {
             if (hasSessionId(sessionId)) {
                 Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
-                halCloseEndpointSessionNoThrow(sessionId, Reason.UNSPECIFIED);
-                return false;
+                return Optional.of(Reason.UNSPECIFIED);
             }
             mSessionInfoMap.put(sessionId, new SessionInfo(initiator, true));
         }
 
-        return invokeCallback(
+        boolean success = invokeCallback(
                 (consumer) ->
                         consumer.onSessionOpenRequest(sessionId, initiator, serviceDescriptor));
+        return success ? Optional.empty() : Optional.of(Reason.UNSPECIFIED);
     }
 
     private byte onMessageReceivedInternal(int sessionId, HubMessage message) {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
index 011659a..3eb38a7 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
@@ -17,6 +17,7 @@
 package com.android.server.media;
 
 import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -217,6 +218,28 @@
         }
     }
 
+    @Override
+    public void setRouteVolume(long requestId, String routeOriginalId, int volume) {
+        synchronized (mLock) {
+            var targetProviderProxyId = mOriginalRouteIdToProviderId.get(routeOriginalId);
+            var targetProviderProxyRecord = mProxyRecords.get(targetProviderProxyId);
+            // Holds the target route, if it's managed by a provider service. Holds null otherwise.
+            if (targetProviderProxyRecord != null) {
+                var serviceTargetRoute =
+                        targetProviderProxyRecord.mNewOriginalIdToSourceOriginalIdMap.get(
+                                routeOriginalId);
+                if (serviceTargetRoute != null) {
+                    targetProviderProxyRecord.mProxy.setRouteVolume(
+                            requestId, serviceTargetRoute, volume);
+                } else {
+                    notifyRequestFailed(
+                            requestId, MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE);
+                }
+            }
+        }
+        super.setRouteVolume(requestId, routeOriginalId, volume);
+    }
+
     /**
      * Returns the uid that corresponds to the given name and user handle, or {@link
      * Process#INVALID_UID} if a uid couldn't be found.
@@ -463,11 +486,18 @@
                 }
                 String id =
                         asSystemRouteId(providerInfo.getUniqueId(), sourceRoute.getOriginalId());
-                var newRoute =
-                        new MediaRoute2Info.Builder(id, sourceRoute.getName())
-                                .addFeature(FEATURE_LIVE_AUDIO)
-                                .build();
-                routesMap.put(id, newRoute);
+                var newRouteBuilder = new MediaRoute2Info.Builder(id, sourceRoute);
+                if ((sourceRoute.getSupportedRoutingTypes()
+                                & MediaRoute2Info.FLAG_ROUTING_TYPE_SYSTEM_AUDIO)
+                        != 0) {
+                    newRouteBuilder.addFeature(FEATURE_LIVE_AUDIO);
+                }
+                if ((sourceRoute.getSupportedRoutingTypes()
+                                & MediaRoute2Info.FLAG_ROUTING_TYPE_SYSTEM_VIDEO)
+                        != 0) {
+                    newRouteBuilder.addFeature(FEATURE_LIVE_VIDEO);
+                }
+                routesMap.put(id, newRouteBuilder.build());
                 idMap.put(id, sourceRoute.getOriginalId());
             }
             return new ProviderProxyRecord(
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
index 2e0bb4f..18f2f48 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
@@ -28,6 +28,7 @@
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyCallback;
@@ -38,6 +39,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.SystemConfig;
 
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
@@ -60,21 +62,35 @@
     private final TelephonyManager mTelephonyManager;
     private final AppOpsManager mAppOpsManager;
     private final PackageManager mPackageManager;
-    private final RoleManager mRoleManager;
+    private final RoleHolderProvider mRoleHolderProvider;
     private final ContentResolver mContentResolver;
 
     private boolean mIsInCall;
     private long mLastCallStartTimeMillis;
 
+
+    @VisibleForTesting
+    interface RoleHolderProvider {
+        List<String> getRoleHoldersAsUser(String roleName, UserHandle user);
+    }
+
     public MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer) {
+        this(context, stopReasonConsumer,
+                (roleName, user) -> context.getSystemService(RoleManager.class)
+                        .getRoleHoldersAsUser(roleName, user));
+    }
+
+    @VisibleForTesting
+    MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer,
+            RoleHolderProvider roleHolderProvider) {
         mStopReasonConsumer = stopReasonConsumer;
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
         mTelecomManager = context.getSystemService(TelecomManager.class);
         mTelephonyManager = context.getSystemService(TelephonyManager.class);
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
         mPackageManager = context.getPackageManager();
-        mRoleManager = context.getSystemService(RoleManager.class);
         mContentResolver = context.getContentResolver();
+        mRoleHolderProvider = roleHolderProvider;
     }
 
     /**
@@ -146,8 +162,9 @@
             Slog.v(TAG, "Continuing MediaProjection for package with OP_PROJECT_MEDIA AppOp ");
             return true;
         }
-        if (mRoleManager.getRoleHoldersAsUser(AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
-                projectionGrant.userHandle).contains(projectionGrant.packageName)) {
+        if (mRoleHolderProvider.getRoleHoldersAsUser(
+                AssociationRequest.DEVICE_PROFILE_APP_STREAMING, projectionGrant.userHandle)
+                .contains(projectionGrant.packageName)) {
             Slog.v(TAG, "Continuing MediaProjection for package holding app streaming role.");
             return true;
         }
@@ -177,10 +194,6 @@
      */
     public boolean isStartForbidden(
             MediaProjectionManagerService.MediaProjection projectionGrant) {
-        if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
-            return false;
-        }
-
         if (!mKeyguardManager.isKeyguardLocked()) {
             return false;
         }
@@ -194,9 +207,6 @@
     @VisibleForTesting
     void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
         if (!isKeyguardLocked) return;
-        if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
-            return;
-        }
         mStopReasonConsumer.accept(STOP_REASON_KEYGUARD);
     }
 
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 2a5b779..d00ac4d 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -30,10 +30,17 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.hardware.tv.mediaquality.AmbientBacklightColorFormat;
 import android.hardware.tv.mediaquality.IMediaQuality;
+import android.hardware.tv.mediaquality.IPictureProfileAdjustmentListener;
+import android.hardware.tv.mediaquality.IPictureProfileChangedListener;
+import android.hardware.tv.mediaquality.ISoundProfileAdjustmentListener;
+import android.hardware.tv.mediaquality.ISoundProfileChangedListener;
+import android.hardware.tv.mediaquality.ParamCapability;
+import android.hardware.tv.mediaquality.ParameterRange;
 import android.hardware.tv.mediaquality.PictureParameter;
 import android.hardware.tv.mediaquality.PictureParameters;
 import android.hardware.tv.mediaquality.SoundParameter;
 import android.hardware.tv.mediaquality.SoundParameters;
+import android.hardware.tv.mediaquality.VendorParamCapability;
 import android.media.quality.AmbientBacklightEvent;
 import android.media.quality.AmbientBacklightMetadata;
 import android.media.quality.AmbientBacklightSettings;
@@ -42,8 +49,6 @@
 import android.media.quality.IPictureProfileCallback;
 import android.media.quality.ISoundProfileCallback;
 import android.media.quality.MediaQualityContract.BaseParameters;
-import android.media.quality.MediaQualityContract.PictureQuality;
-import android.media.quality.MediaQualityContract.SoundQuality;
 import android.media.quality.MediaQualityManager;
 import android.media.quality.ParameterCapability;
 import android.media.quality.PictureProfile;
@@ -65,24 +70,20 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
 import com.android.server.utils.Slogf;
 
-import org.json.JSONException;
-import org.json.JSONObject;
-
 import java.io.File;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
-import java.util.UUID;
 import java.util.stream.Collectors;
 
 /**
@@ -97,12 +98,15 @@
     private static final String PICTURE_PROFILE_PREFERENCE = "picture_profile_preference";
     private static final String SOUND_PROFILE_PREFERENCE = "sound_profile_preference";
     private static final String COMMA_DELIMITER = ",";
-    private static final int MAX_UUID_GENERATION_ATTEMPTS = 10;
     private final Context mContext;
     private final MediaQualityDbHelper mMediaQualityDbHelper;
     private final BiMap<Long, String> mPictureProfileTempIdMap;
     private final BiMap<Long, String> mSoundProfileTempIdMap;
     private IMediaQuality mMediaQuality;
+    private IPictureProfileAdjustmentListener mPpAdjustmentListener;
+    private ISoundProfileAdjustmentListener mSpAdjustmentListener;
+    private IPictureProfileChangedListener mPpChangedListener;
+    private ISoundProfileChangedListener mSpChangedListener;
     private final HalAmbientBacklightCallback mHalAmbientBacklightCallback;
     private final Map<String, AmbientBacklightCallbackRecord> mCallbackRecords = new HashMap<>();
     private final PackageManager mPackageManager;
@@ -110,6 +114,13 @@
     private SharedPreferences mPictureProfileSharedPreference;
     private SharedPreferences mSoundProfileSharedPreference;
 
+    // A global lock for picture profile objects.
+    private final Object mPictureProfileLock = new Object();
+    // A global lock for sound profile objects.
+    private final Object mSoundProfileLock = new Object();
+    // A global lock for ambient backlight objects.
+    private final Object mAmbientBacklightLock = new Object();
+
     public MediaQualityService(Context context) {
         super(context);
         mContext = context;
@@ -138,24 +149,111 @@
     @Override
     public void onStart() {
         IBinder binder = ServiceManager.getService(IMediaQuality.DESCRIPTOR + "/default");
-        if (binder != null) {
-            Slogf.d(TAG, "binder is not null");
-            mMediaQuality = IMediaQuality.Stub.asInterface(binder);
-            if (mMediaQuality != null) {
-                try {
-                    mMediaQuality.setAmbientBacklightCallback(mHalAmbientBacklightCallback);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Failed to set ambient backlight detector callback", e);
+        if (binder == null) {
+            Slogf.d(TAG, "Binder is null");
+            return;
+        }
+        Slogf.d(TAG, "Binder is not null");
+
+        mPpAdjustmentListener = new IPictureProfileAdjustmentListener.Stub() {
+                @Override
+                public void onPictureProfileAdjusted(
+                        android.hardware.tv.mediaquality.PictureProfile pictureProfile)
+                        throws RemoteException {
+                    // TODO
                 }
+
+                @Override
+                public void onParamCapabilityChanged(long pictureProfileId, ParamCapability[] caps)
+                        throws RemoteException {
+                    // TODO
+                }
+
+                @Override
+                public void onVendorParamCapabilityChanged(long pictureProfileId,
+                        VendorParamCapability[] caps) throws RemoteException {
+                    // TODO
+                }
+
+                @Override
+                public void requestPictureParameters(long pictureProfileId) throws RemoteException {
+                    // TODO
+                }
+
+                @Override
+                public void onStreamStatusChanged(long pictureProfileId, byte status)
+                        throws RemoteException {
+                    // TODO
+                }
+
+                @Override
+                public int getInterfaceVersion() throws RemoteException {
+                    return 0;
+                }
+
+                @Override
+                public String getInterfaceHash() throws RemoteException {
+                    return null;
+                }
+            };
+        mSpAdjustmentListener = new ISoundProfileAdjustmentListener.Stub() {
+
+                @Override
+                public void onSoundProfileAdjusted(
+                        android.hardware.tv.mediaquality.SoundProfile soundProfile)
+                        throws RemoteException {
+                    // TODO
+                }
+
+                @Override
+                public void onParamCapabilityChanged(long soundProfileId, ParamCapability[] caps)
+                        throws RemoteException {
+                    // TODO
+                }
+
+                @Override
+                public void onVendorParamCapabilityChanged(long soundProfileId,
+                        VendorParamCapability[] caps) throws RemoteException {
+                    // TODO
+                }
+
+                @Override
+                public void requestSoundParameters(long soundProfileId) throws RemoteException {
+                    // TODO
+                }
+
+                @Override
+                public int getInterfaceVersion() throws RemoteException {
+                    return 0;
+                }
+
+                @Override
+                public String getInterfaceHash() throws RemoteException {
+                    return null;
+                }
+            };
+
+        mMediaQuality = IMediaQuality.Stub.asInterface(binder);
+        if (mMediaQuality != null) {
+            try {
+                mMediaQuality.setAmbientBacklightCallback(mHalAmbientBacklightCallback);
+                mMediaQuality.setPictureProfileAdjustmentListener(mPpAdjustmentListener);
+                mMediaQuality.setSoundProfileAdjustmentListener(mSpAdjustmentListener);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to set ambient backlight detector callback", e);
             }
         }
 
+        mPpChangedListener = IPictureProfileChangedListener.Stub.asInterface(binder);
+        mSpChangedListener = ISoundProfileChangedListener.Stub.asInterface(binder);
+
         publishBinderService(Context.MEDIA_QUALITY_SERVICE, new BinderService());
     }
 
     // TODO: Add additional APIs. b/373951081
     private final class BinderService extends IMediaQualityManager.Stub {
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public PictureProfile createPictureProfile(PictureProfile pp, UserHandle user) {
             if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
@@ -165,26 +263,55 @@
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
-            SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+            synchronized (mPictureProfileLock) {
+                SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
 
-            ContentValues values = getContentValues(null,
-                    pp.getProfileType(),
-                    pp.getName(),
-                    pp.getPackageName() == null || pp.getPackageName().isEmpty()
-                            ? getPackageOfCallingUid() : pp.getPackageName(),
-                    pp.getInputId(),
-                    pp.getParameters());
+                ContentValues values = MediaQualityUtils.getContentValues(null,
+                        pp.getProfileType(),
+                        pp.getName(),
+                        pp.getPackageName() == null || pp.getPackageName().isEmpty()
+                                ? getPackageOfCallingUid() : pp.getPackageName(),
+                        pp.getInputId(),
+                        pp.getParameters());
 
-            // id is auto-generated by SQLite upon successful insertion of row
-            Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
-                    null, values);
-            populateTempIdMap(mPictureProfileTempIdMap, id);
-            String value = mPictureProfileTempIdMap.getValue(id);
-            pp.setProfileId(value);
-            notifyOnPictureProfileAdded(value, pp, Binder.getCallingUid(), Binder.getCallingPid());
-            return pp;
+                // id is auto-generated by SQLite upon successful insertion of row
+                Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
+                        null, values);
+                MediaQualityUtils.populateTempIdMap(mPictureProfileTempIdMap, id);
+                String value = mPictureProfileTempIdMap.getValue(id);
+                pp.setProfileId(value);
+                notifyOnPictureProfileAdded(value, pp, Binder.getCallingUid(),
+                        Binder.getCallingPid());
+                return pp;
+            }
         }
 
+        private void notifyHalOnPictureProfileChange(Long dbId, PersistableBundle params) {
+            // TODO: only notify HAL when the profile is active / being used
+            try {
+                mPpChangedListener.onPictureProfileChanged(convertToHalPictureProfile(dbId,
+                        params));
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to notify HAL on picture profile change.", e);
+            }
+        }
+
+        private android.hardware.tv.mediaquality.PictureProfile convertToHalPictureProfile(Long id,
+                PersistableBundle params) {
+            PictureParameters pictureParameters = new PictureParameters();
+            pictureParameters.pictureParameters =
+                    MediaQualityUtils.convertPersistableBundleToPictureParameterList(
+                            params);
+
+            android.hardware.tv.mediaquality.PictureProfile toReturn =
+                    new android.hardware.tv.mediaquality.PictureProfile();
+            toReturn.pictureProfileId = id;
+            toReturn.parameters = pictureParameters;
+
+            return toReturn;
+        }
+
+        @GuardedBy("mPictureProfileLock")
         @Override
         public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
             Long dbId = mPictureProfileTempIdMap.getKey(id);
@@ -193,18 +320,21 @@
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
-            ContentValues values = getContentValues(dbId,
-                    pp.getProfileType(),
-                    pp.getName(),
-                    pp.getPackageName(),
-                    pp.getInputId(),
-                    pp.getParameters());
+            synchronized (mPictureProfileLock) {
+                ContentValues values = MediaQualityUtils.getContentValues(dbId,
+                        pp.getProfileType(),
+                        pp.getName(),
+                        pp.getPackageName(),
+                        pp.getInputId(),
+                        pp.getParameters());
 
             SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
             db.replace(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
                     null, values);
             notifyOnPictureProfileUpdated(mPictureProfileTempIdMap.getValue(dbId),
                     getPictureProfile(dbId), Binder.getCallingUid(), Binder.getCallingPid());
+            notifyHalOnPictureProfileChange(dbId, pp.getParameters());
+            }
         }
 
         private boolean hasPermissionToUpdatePictureProfile(Long dbId, PictureProfile toUpdate) {
@@ -215,29 +345,33 @@
                     && fromDb.getName().equals(getPackageOfCallingUid());
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public void removePictureProfile(String id, UserHandle user) {
-            Long dbId = mPictureProfileTempIdMap.getKey(id);
+            synchronized (mPictureProfileLock) {
+                Long dbId = mPictureProfileTempIdMap.getKey(id);
 
-            PictureProfile toDelete = getPictureProfile(dbId);
-            if (!hasPermissionToRemovePictureProfile(toDelete)) {
-                notifyOnPictureProfileError(id, PictureProfile.ERROR_NO_PERMISSION,
-                        Binder.getCallingUid(), Binder.getCallingPid());
-            }
-
-            if (dbId != null) {
-                SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
-                String selection = BaseParameters.PARAMETER_ID + " = ?";
-                String[] selectionArgs = {Long.toString(dbId)};
-                int result = db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection,
-                        selectionArgs);
-                if (result == 0) {
-                    notifyOnPictureProfileError(id, PictureProfile.ERROR_INVALID_ARGUMENT,
+                PictureProfile toDelete = getPictureProfile(dbId);
+                if (!hasPermissionToRemovePictureProfile(toDelete)) {
+                    notifyOnPictureProfileError(id, PictureProfile.ERROR_NO_PERMISSION,
                             Binder.getCallingUid(), Binder.getCallingPid());
                 }
-                notifyOnPictureProfileRemoved(mPictureProfileTempIdMap.getValue(dbId), toDelete,
-                        Binder.getCallingUid(), Binder.getCallingPid());
-                mPictureProfileTempIdMap.remove(dbId);
+
+                if (dbId != null) {
+                    SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+                    String selection = BaseParameters.PARAMETER_ID + " = ?";
+                    String[] selectionArgs = {Long.toString(dbId)};
+                    int result = db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
+                            selection, selectionArgs);
+                    if (result == 0) {
+                        notifyOnPictureProfileError(id, PictureProfile.ERROR_INVALID_ARGUMENT,
+                                Binder.getCallingUid(), Binder.getCallingPid());
+                    }
+                    notifyOnPictureProfileRemoved(mPictureProfileTempIdMap.getValue(dbId), toDelete,
+                            Binder.getCallingUid(), Binder.getCallingPid());
+                    mPictureProfileTempIdMap.remove(dbId);
+                    notifyHalOnPictureProfileChange(dbId, null);
+                }
             }
         }
 
@@ -248,6 +382,7 @@
             return false;
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public PictureProfile getPictureProfile(int type, String name, Bundle options,
                 UserHandle user) {
@@ -258,23 +393,28 @@
                     + BaseParameters.PARAMETER_PACKAGE + " = ?";
             String[] selectionArguments = {Integer.toString(type), name, getPackageOfCallingUid()};
 
-            try (
-                    Cursor cursor = getCursorAfterQuerying(
-                            mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
-                            getMediaProfileColumns(includeParams), selection, selectionArguments)
-            ) {
-                int count = cursor.getCount();
-                if (count == 0) {
-                    return null;
+            synchronized (mPictureProfileLock) {
+                try (
+                        Cursor cursor = getCursorAfterQuerying(
+                                mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
+                                MediaQualityUtils.getMediaProfileColumns(includeParams), selection,
+                                selectionArguments)
+                ) {
+                    int count = cursor.getCount();
+                    if (count == 0) {
+                        return null;
+                    }
+                    if (count > 1) {
+                        Log.wtf(TAG, TextUtils.formatSimple(String.valueOf(Locale.US), "%d "
+                                        + "entries found for type=%d and name=%s in %s. Should"
+                                        + " only ever be 0 or 1.", count, type, name,
+                                mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME));
+                        return null;
+                    }
+                    cursor.moveToFirst();
+                    return MediaQualityUtils.convertCursorToPictureProfileWithTempId(cursor,
+                            mPictureProfileTempIdMap);
                 }
-                if (count > 1) {
-                    Log.wtf(TAG, String.format(Locale.US, "%d entries found for type=%d and name=%s"
-                                    + " in %s. Should only ever be 0 or 1.", count, type, name,
-                            mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME));
-                    return null;
-                }
-                cursor.moveToFirst();
-                return convertCursorToPictureProfileWithTempId(cursor);
             }
         }
 
@@ -285,23 +425,26 @@
             try (
                     Cursor cursor = getCursorAfterQuerying(
                             mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
-                            getMediaProfileColumns(false), selection, selectionArguments)
+                            MediaQualityUtils.getMediaProfileColumns(false), selection,
+                            selectionArguments)
             ) {
                 int count = cursor.getCount();
                 if (count == 0) {
                     return null;
                 }
                 if (count > 1) {
-                    Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%d"
-                                    + " in %s. Should only ever be 0 or 1.", count, dbId,
-                            mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME));
+                    Log.wtf(TAG, TextUtils.formatSimple(String.valueOf(Locale.US), "%d entries "
+                                    + "found for id=%d in %s. Should only ever be 0 or 1.",
+                            count, dbId, mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME));
                     return null;
                 }
                 cursor.moveToFirst();
-                return convertCursorToPictureProfileWithTempId(cursor);
+                return MediaQualityUtils.convertCursorToPictureProfileWithTempId(cursor,
+                        mPictureProfileTempIdMap);
             }
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public List<PictureProfile> getPictureProfilesByPackage(
                 String packageName, Bundle options, UserHandle user) {
@@ -310,14 +453,18 @@
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
-            boolean includeParams =
-                    options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
-            String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
-            String[] selectionArguments = {packageName};
-            return getPictureProfilesBasedOnConditions(getMediaProfileColumns(includeParams),
-                    selection, selectionArguments);
+            synchronized (mPictureProfileLock) {
+                boolean includeParams =
+                        options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
+                String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
+                String[] selectionArguments = {packageName};
+                return getPictureProfilesBasedOnConditions(MediaQualityUtils
+                                .getMediaProfileColumns(includeParams),
+                        selection, selectionArguments);
+            }
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public List<PictureProfile> getAvailablePictureProfiles(Bundle options, UserHandle user) {
             String packageName = getPackageOfCallingUid();
@@ -327,6 +474,7 @@
             return new ArrayList<>();
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public boolean setDefaultPictureProfile(String profileId, UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
@@ -340,8 +488,8 @@
 
             try {
                 if (mMediaQuality != null) {
-                    PictureParameter[] pictureParameters =
-                            convertPersistableBundleToPictureParameterList(params);
+                    PictureParameter[] pictureParameters = MediaQualityUtils
+                            .convertPersistableBundleToPictureParameterList(params);
 
                     PictureParameters pp = new PictureParameters();
                     pp.pictureParameters = pictureParameters;
@@ -355,186 +503,7 @@
             return false;
         }
 
-        private PictureParameter[] convertPersistableBundleToPictureParameterList(
-                PersistableBundle params) {
-            List<PictureParameter> pictureParams = new ArrayList<>();
-            if (params.containsKey(PictureQuality.PARAMETER_BRIGHTNESS)) {
-                pictureParams.add(PictureParameter.brightness(params.getLong(
-                        PictureQuality.PARAMETER_BRIGHTNESS)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_CONTRAST)) {
-                pictureParams.add(PictureParameter.contrast(params.getInt(
-                        PictureQuality.PARAMETER_CONTRAST)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_SHARPNESS)) {
-                pictureParams.add(PictureParameter.sharpness(params.getInt(
-                        PictureQuality.PARAMETER_SHARPNESS)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_SATURATION)) {
-                pictureParams.add(PictureParameter.saturation(params.getInt(
-                        PictureQuality.PARAMETER_SATURATION)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_HUE)) {
-                pictureParams.add(PictureParameter.hue(params.getInt(
-                        PictureQuality.PARAMETER_HUE)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)) {
-                pictureParams.add(PictureParameter.colorTunerBrightness(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)) {
-                pictureParams.add(PictureParameter.colorTunerSaturation(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE)) {
-                pictureParams.add(PictureParameter.colorTunerHue(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_HUE)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)) {
-                pictureParams.add(PictureParameter.colorTunerRedOffset(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)) {
-                pictureParams.add(PictureParameter.colorTunerGreenOffset(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)) {
-                pictureParams.add(PictureParameter.colorTunerBlueOffset(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
-                pictureParams.add(PictureParameter.colorTunerRedGain(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
-                pictureParams.add(PictureParameter.colorTunerGreenGain(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
-                pictureParams.add(PictureParameter.colorTunerBlueGain(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_NOISE_REDUCTION)) {
-                pictureParams.add(PictureParameter.noiseReduction(
-                        (byte) params.getInt(PictureQuality.PARAMETER_NOISE_REDUCTION)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) {
-                pictureParams.add(PictureParameter.mpegNoiseReduction(
-                        (byte) params.getInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_FLESH_TONE)) {
-                pictureParams.add(PictureParameter.fleshTone(
-                        (byte) params.getInt(PictureQuality.PARAMETER_FLESH_TONE)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_DECONTOUR)) {
-                pictureParams.add(PictureParameter.deContour(
-                        (byte) params.getInt(PictureQuality.PARAMETER_DECONTOUR)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) {
-                pictureParams.add(PictureParameter.dynamicLumaControl(
-                        (byte) params.getInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_FILM_MODE)) {
-                pictureParams.add(PictureParameter.filmMode(params.getBoolean(
-                        PictureQuality.PARAMETER_FILM_MODE)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_BLUE_STRETCH)) {
-                pictureParams.add(PictureParameter.blueStretch(params.getBoolean(
-                        PictureQuality.PARAMETER_BLUE_STRETCH)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNE)) {
-                pictureParams.add(PictureParameter.colorTune(params.getBoolean(
-                        PictureQuality.PARAMETER_COLOR_TUNE)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) {
-                pictureParams.add(PictureParameter.colorTemperature(
-                        (byte) params.getInt(
-                                PictureQuality.PARAMETER_COLOR_TEMPERATURE)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_GLOBAL_DIMMING)) {
-                pictureParams.add(PictureParameter.globeDimming(params.getBoolean(
-                        PictureQuality.PARAMETER_GLOBAL_DIMMING)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)) {
-                pictureParams.add(PictureParameter.autoPictureQualityEnabled(params.getBoolean(
-                        PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)) {
-                pictureParams.add(PictureParameter.autoSuperResolutionEnabled(params.getBoolean(
-                        PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
-                pictureParams.add(PictureParameter.colorTemperatureRedGain(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
-                pictureParams.add(PictureParameter.colorTemperatureGreenGain(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
-            }
-            if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
-                pictureParams.add(PictureParameter.colorTemperatureBlueGain(params.getInt(
-                        PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
-            }
-
-            /**
-             * TODO: add conversion for following after adding to MediaQualityContract
-             *
-             * PictureParameter.levelRange
-             * PictureParameter.gamutMapping
-             * PictureParameter.pcMode
-             * PictureParameter.lowLatency
-             * PictureParameter.vrr
-             * PictureParameter.cvrr
-             * PictureParameter.hdmiRgbRange
-             * PictureParameter.colorSpace
-             * PictureParameter.panelInitMaxLuminceNits
-             * PictureParameter.panelInitMaxLuminceValid
-             * PictureParameter.gamma
-             * PictureParameter.colorTemperatureRedOffset
-             * PictureParameter.colorTemperatureGreenOffset
-             * PictureParameter.colorTemperatureBlueOffset
-             * PictureParameter.elevenPointRed
-             * PictureParameter.elevenPointGreen
-             * PictureParameter.elevenPointBlue
-             * PictureParameter.lowBlueLight
-             * PictureParameter.LdMode
-             * PictureParameter.osdRedGain
-             * PictureParameter.osdGreenGain
-             * PictureParameter.osdBlueGain
-             * PictureParameter.osdRedOffset
-             * PictureParameter.osdGreenOffset
-             * PictureParameter.osdBlueOffset
-             * PictureParameter.osdHue
-             * PictureParameter.osdSaturation
-             * PictureParameter.osdContrast
-             * PictureParameter.colorTunerSwitch
-             * PictureParameter.colorTunerHueRed
-             * PictureParameter.colorTunerHueGreen
-             * PictureParameter.colorTunerHueBlue
-             * PictureParameter.colorTunerHueCyan
-             * PictureParameter.colorTunerHueMagenta
-             * PictureParameter.colorTunerHueYellow
-             * PictureParameter.colorTunerHueFlesh
-             * PictureParameter.colorTunerSaturationRed
-             * PictureParameter.colorTunerSaturationGreen
-             * PictureParameter.colorTunerSaturationBlue
-             * PictureParameter.colorTunerSaturationCyan
-             * PictureParameter.colorTunerSaturationMagenta
-             * PictureParameter.colorTunerSaturationYellow
-             * PictureParameter.colorTunerSaturationFlesh
-             * PictureParameter.colorTunerLuminanceRed
-             * PictureParameter.colorTunerLuminanceGreen
-             * PictureParameter.colorTunerLuminanceBlue
-             * PictureParameter.colorTunerLuminanceCyan
-             * PictureParameter.colorTunerLuminanceMagenta
-             * PictureParameter.colorTunerLuminanceYellow
-             * PictureParameter.colorTunerLuminanceFlesh
-             * PictureParameter.activeProfile
-             * PictureParameter.pictureQualityEventType
-             */
-            return  (PictureParameter[]) pictureParams.toArray();
-        }
-
+        @GuardedBy("mPictureProfileLock")
         @Override
         public List<String> getPictureProfilePackageNames(UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
@@ -542,42 +511,51 @@
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
             String [] column = {BaseParameters.PARAMETER_PACKAGE};
-            List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
-                    null, null);
-            return pictureProfiles.stream()
-                    .map(PictureProfile::getPackageName)
-                    .distinct()
-                    .collect(Collectors.toList());
+            synchronized (mPictureProfileLock) {
+                List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
+                        null, null);
+                return pictureProfiles.stream()
+                        .map(PictureProfile::getPackageName)
+                        .distinct()
+                        .collect(Collectors.toList());
+            }
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public List<PictureProfileHandle> getPictureProfileHandle(String[] ids, UserHandle user) {
             List<PictureProfileHandle> toReturn = new ArrayList<>();
-            for (String id : ids) {
-                Long key = mPictureProfileTempIdMap.getKey(id);
-                if (key != null) {
-                    toReturn.add(new PictureProfileHandle(key));
-                } else {
-                    toReturn.add(null);
+            synchronized (mPictureProfileLock) {
+                for (String id : ids) {
+                    Long key = mPictureProfileTempIdMap.getKey(id);
+                    if (key != null) {
+                        toReturn.add(new PictureProfileHandle(key));
+                    } else {
+                        toReturn.add(null);
+                    }
                 }
             }
             return toReturn;
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public List<SoundProfileHandle> getSoundProfileHandle(String[] ids, UserHandle user) {
             List<SoundProfileHandle> toReturn = new ArrayList<>();
-            for (String id : ids) {
-                Long key = mSoundProfileTempIdMap.getKey(id);
-                if (key != null) {
-                    toReturn.add(new SoundProfileHandle(key));
-                } else {
-                    toReturn.add(null);
+            synchronized (mSoundProfileLock) {
+                for (String id : ids) {
+                    Long key = mSoundProfileTempIdMap.getKey(id);
+                    if (key != null) {
+                        toReturn.add(new SoundProfileHandle(key));
+                    } else {
+                        toReturn.add(null);
+                    }
                 }
             }
             return toReturn;
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public SoundProfile createSoundProfile(SoundProfile sp, UserHandle user) {
             if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty()
@@ -586,26 +564,54 @@
                 notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
-            SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
 
-            ContentValues values = getContentValues(null,
-                    sp.getProfileType(),
-                    sp.getName(),
-                    sp.getPackageName() == null || sp.getPackageName().isEmpty()
-                            ? getPackageOfCallingUid() : sp.getPackageName(),
-                    sp.getInputId(),
-                    sp.getParameters());
+            synchronized (mSoundProfileLock) {
+                SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
 
-            // id is auto-generated by SQLite upon successful insertion of row
-            Long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
-                    null, values);
-            populateTempIdMap(mSoundProfileTempIdMap, id);
-            String value = mSoundProfileTempIdMap.getValue(id);
-            sp.setProfileId(value);
-            notifyOnSoundProfileAdded(value, sp, Binder.getCallingUid(), Binder.getCallingPid());
-            return sp;
+                ContentValues values = MediaQualityUtils.getContentValues(null,
+                        sp.getProfileType(),
+                        sp.getName(),
+                        sp.getPackageName() == null || sp.getPackageName().isEmpty()
+                                ? getPackageOfCallingUid() : sp.getPackageName(),
+                        sp.getInputId(),
+                        sp.getParameters());
+
+                // id is auto-generated by SQLite upon successful insertion of row
+                Long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
+                        null, values);
+                MediaQualityUtils.populateTempIdMap(mSoundProfileTempIdMap, id);
+                String value = mSoundProfileTempIdMap.getValue(id);
+                sp.setProfileId(value);
+                notifyOnSoundProfileAdded(value, sp, Binder.getCallingUid(),
+                        Binder.getCallingPid());
+                return sp;
+            }
         }
 
+        private void notifyHalOnSoundProfileChange(Long dbId, PersistableBundle params) {
+            // TODO: only notify HAL when the profile is active / being used
+            try {
+                mSpChangedListener.onSoundProfileChanged(convertToHalSoundProfile(dbId, params));
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to notify HAL on sound profile change.", e);
+            }
+        }
+
+        private android.hardware.tv.mediaquality.SoundProfile convertToHalSoundProfile(Long id,
+                PersistableBundle params) {
+            SoundParameters soundParameters = new SoundParameters();
+            soundParameters.soundParameters =
+                    MediaQualityUtils.convertPersistableBundleToSoundParameterList(params);
+
+            android.hardware.tv.mediaquality.SoundProfile toReturn =
+                    new android.hardware.tv.mediaquality.SoundProfile();
+            toReturn.soundProfileId = id;
+            toReturn.parameters = soundParameters;
+
+            return toReturn;
+        }
+
+        @GuardedBy("mSoundProfileLock")
         @Override
         public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) {
             Long dbId = mSoundProfileTempIdMap.getKey(id);
@@ -614,17 +620,21 @@
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
-            ContentValues values = getContentValues(dbId,
-                    sp.getProfileType(),
-                    sp.getName(),
-                    sp.getPackageName(),
-                    sp.getInputId(),
-                    sp.getParameters());
+            synchronized (mSoundProfileLock) {
+                ContentValues values = MediaQualityUtils.getContentValues(dbId,
+                        sp.getProfileType(),
+                        sp.getName(),
+                        sp.getPackageName(),
+                        sp.getInputId(),
+                        sp.getParameters());
 
-            SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
-            db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
-            notifyOnSoundProfileUpdated(mSoundProfileTempIdMap.getValue(dbId),
-                    getSoundProfile(dbId), Binder.getCallingUid(), Binder.getCallingPid());
+                SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+                db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
+                        null, values);
+                notifyOnSoundProfileUpdated(mSoundProfileTempIdMap.getValue(dbId),
+                        getSoundProfile(dbId), Binder.getCallingUid(), Binder.getCallingPid());
+                notifyHalOnSoundProfileChange(dbId, sp.getParameters());
+            }
         }
 
         private boolean hasPermissionToUpdateSoundProfile(Long dbId, SoundProfile sp) {
@@ -635,28 +645,32 @@
                     && fromDb.getName().equals(getPackageOfCallingUid());
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public void removeSoundProfile(String id, UserHandle user) {
-            Long dbId = mSoundProfileTempIdMap.getKey(id);
-            SoundProfile toDelete = getSoundProfile(dbId);
-            if (!hasPermissionToRemoveSoundProfile(toDelete)) {
-                notifyOnSoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
-                        Binder.getCallingUid(), Binder.getCallingPid());
-            }
-
-            if (dbId != null) {
-                SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
-                String selection = BaseParameters.PARAMETER_ID + " = ?";
-                String[] selectionArgs = {Long.toString(dbId)};
-                int result = db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection,
-                        selectionArgs);
-                if (result == 0) {
-                    notifyOnSoundProfileError(id, SoundProfile.ERROR_INVALID_ARGUMENT,
+            synchronized (mSoundProfileLock) {
+                Long dbId = mSoundProfileTempIdMap.getKey(id);
+                SoundProfile toDelete = getSoundProfile(dbId);
+                if (!hasPermissionToRemoveSoundProfile(toDelete)) {
+                    notifyOnSoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
                             Binder.getCallingUid(), Binder.getCallingPid());
                 }
-                notifyOnSoundProfileRemoved(mSoundProfileTempIdMap.getValue(dbId), toDelete,
-                        Binder.getCallingUid(), Binder.getCallingPid());
-                mSoundProfileTempIdMap.remove(dbId);
+                if (dbId != null) {
+                    SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+                    String selection = BaseParameters.PARAMETER_ID + " = ?";
+                    String[] selectionArgs = {Long.toString(dbId)};
+                    int result = db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
+                            selection,
+                            selectionArgs);
+                    if (result == 0) {
+                        notifyOnSoundProfileError(id, SoundProfile.ERROR_INVALID_ARGUMENT,
+                                Binder.getCallingUid(), Binder.getCallingPid());
+                    }
+                    notifyOnSoundProfileRemoved(mSoundProfileTempIdMap.getValue(dbId), toDelete,
+                            Binder.getCallingUid(), Binder.getCallingPid());
+                    mSoundProfileTempIdMap.remove(dbId);
+                    notifyHalOnSoundProfileChange(dbId, null);
+                }
             }
         }
 
@@ -667,6 +681,7 @@
             return false;
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public SoundProfile getSoundProfile(int type, String name, Bundle options,
                 UserHandle user) {
@@ -677,23 +692,28 @@
                     + BaseParameters.PARAMETER_PACKAGE + " = ?";
             String[] selectionArguments = {String.valueOf(type), name, getPackageOfCallingUid()};
 
-            try (
-                    Cursor cursor = getCursorAfterQuerying(
-                            mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
-                            getMediaProfileColumns(includeParams), selection, selectionArguments)
-            ) {
-                int count = cursor.getCount();
-                if (count == 0) {
-                    return null;
+            synchronized (mSoundProfileLock) {
+                try (
+                        Cursor cursor = getCursorAfterQuerying(
+                                mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
+                                MediaQualityUtils.getMediaProfileColumns(includeParams), selection,
+                                selectionArguments)
+                ) {
+                    int count = cursor.getCount();
+                    if (count == 0) {
+                        return null;
+                    }
+                    if (count > 1) {
+                        Log.wtf(TAG, TextUtils.formatSimple(String.valueOf(Locale.US), "%d "
+                                        + "entries found for name=%s in %s. Should only ever "
+                                        + "be 0 or 1.", String.valueOf(count), name,
+                                mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME));
+                        return null;
+                    }
+                    cursor.moveToFirst();
+                    return MediaQualityUtils.convertCursorToSoundProfileWithTempId(cursor,
+                            mSoundProfileTempIdMap);
                 }
-                if (count > 1) {
-                    Log.wtf(TAG, String.format(Locale.US, "%d entries found for name=%s"
-                                    + " in %s. Should only ever be 0 or 1.", count, name,
-                            mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME));
-                    return null;
-                }
-                cursor.moveToFirst();
-                return convertCursorToSoundProfileWithTempId(cursor);
             }
         }
 
@@ -704,23 +724,26 @@
             try (
                     Cursor cursor = getCursorAfterQuerying(
                             mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
-                            getMediaProfileColumns(false), selection, selectionArguments)
+                            MediaQualityUtils.getMediaProfileColumns(false), selection,
+                            selectionArguments)
             ) {
                 int count = cursor.getCount();
                 if (count == 0) {
                     return null;
                 }
                 if (count > 1) {
-                    Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%s "
-                                    + "in %s. Should only ever be 0 or 1.", count, dbId,
-                            mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME));
+                    Log.wtf(TAG, TextUtils.formatSimple(String.valueOf(Locale.US), "%d entries "
+                                    + "found for id=%s in %s. Should only ever be 0 or 1.", count,
+                            dbId, mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME));
                     return null;
                 }
                 cursor.moveToFirst();
-                return convertCursorToSoundProfileWithTempId(cursor);
+                return MediaQualityUtils.convertCursorToSoundProfileWithTempId(
+                        cursor, mSoundProfileTempIdMap);
             }
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public List<SoundProfile> getSoundProfilesByPackage(
                 String packageName, Bundle options, UserHandle user) {
@@ -729,14 +752,18 @@
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
-            boolean includeParams =
-                    options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
-            String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
-            String[] selectionArguments = {packageName};
-            return getSoundProfilesBasedOnConditions(getMediaProfileColumns(includeParams),
-                    selection, selectionArguments);
+            synchronized (mSoundProfileLock) {
+                boolean includeParams =
+                        options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
+                String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
+                String[] selectionArguments = {packageName};
+                return getSoundProfilesBasedOnConditions(MediaQualityUtils
+                                .getMediaProfileColumns(includeParams),
+                        selection, selectionArguments);
+            }
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public List<SoundProfile> getAvailableSoundProfiles(Bundle options, UserHandle user) {
             String packageName = getPackageOfCallingUid();
@@ -746,6 +773,7 @@
             return new ArrayList<>();
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public boolean setDefaultSoundProfile(String profileId, UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
@@ -759,7 +787,7 @@
             try {
                 if (mMediaQuality != null) {
                     SoundParameter[] soundParameters =
-                            convertPersistableBundleToSoundParameterList(params);
+                            MediaQualityUtils.convertPersistableBundleToSoundParameterList(params);
 
                     SoundParameters sp = new SoundParameters();
                     sp.soundParameters = soundParameters;
@@ -773,56 +801,7 @@
             return false;
         }
 
-        private SoundParameter[] convertPersistableBundleToSoundParameterList(
-                PersistableBundle params) {
-            List<SoundParameter> soundParams = new ArrayList<>();
-            if (params.containsKey(SoundQuality.PARAMETER_BALANCE)) {
-                soundParams.add(SoundParameter.balance(params.getInt(
-                        SoundQuality.PARAMETER_BALANCE)));
-            }
-            if (params.containsKey(SoundQuality.PARAMETER_BASS)) {
-                soundParams.add(SoundParameter.bass(params.getInt(SoundQuality.PARAMETER_BASS)));
-            }
-            if (params.containsKey(SoundQuality.PARAMETER_TREBLE)) {
-                soundParams.add(SoundParameter.treble(params.getInt(
-                        SoundQuality.PARAMETER_TREBLE)));
-            }
-            if (params.containsKey(SoundQuality.PARAMETER_SURROUND_SOUND)) {
-                soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
-                        SoundQuality.PARAMETER_SURROUND_SOUND)));
-            }
-            if (params.containsKey(SoundQuality.PARAMETER_SPEAKERS)) {
-                soundParams.add(SoundParameter.speakersEnabled(params.getBoolean(
-                        SoundQuality.PARAMETER_SPEAKERS)));
-            }
-            if (params.containsKey(SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)) {
-                soundParams.add(SoundParameter.speakersDelayMs(params.getInt(
-                        SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)));
-            }
-            if (params.containsKey(SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)) {
-                soundParams.add(SoundParameter.autoVolumeControl(params.getBoolean(
-                        SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)));
-            }
-            if (params.containsKey(SoundQuality.PARAMETER_DTS_DRC)) {
-                soundParams.add(SoundParameter.dtsDrc(params.getBoolean(
-                        SoundQuality.PARAMETER_DTS_DRC)));
-            }
-            if (params.containsKey(SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)) {
-                soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
-                        SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)));
-            }
-            //TODO: equalizerDetail
-            //TODO: downmixMode
-            //TODO: enhancedAudioReturnChannelEnabled
-            //TODO: dolbyAudioProcessing
-            //TODO: dolbyDialogueEnhancer
-            //TODO: dtsVirtualX
-            //TODO: digitalOutput
-            //TODO: activeProfile
-            //TODO: soundStyle
-            return  (SoundParameter[]) soundParams.toArray();
-        }
-
+        @GuardedBy("mSoundProfileLock")
         @Override
         public List<String> getSoundProfilePackageNames(UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
@@ -830,12 +809,15 @@
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
             String [] column = {BaseParameters.PARAMETER_NAME};
-            List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
-                    null, null);
-            return soundProfiles.stream()
-                    .map(SoundProfile::getPackageName)
-                    .distinct()
-                    .collect(Collectors.toList());
+
+            synchronized (mSoundProfileLock) {
+                List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
+                        null, null);
+                return soundProfiles.stream()
+                        .map(SoundProfile::getPackageName)
+                        .distinct()
+                        .collect(Collectors.toList());
+            }
         }
 
         private String getPackageOfCallingUid() {
@@ -869,169 +851,6 @@
                     mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED;
         }
 
-        private void populateTempIdMap(BiMap<Long, String> map, Long id) {
-            if (id != null && map.getValue(id) == null) {
-                String uuid;
-                int attempts = 0;
-                while (attempts < MAX_UUID_GENERATION_ATTEMPTS) {
-                    uuid = UUID.randomUUID().toString();
-                    if (map.getKey(uuid) == null) {
-                        map.put(id, uuid);
-                        return;
-                    }
-                    attempts++;
-                }
-            }
-        }
-
-        private String persistableBundleToJson(PersistableBundle bundle) {
-            JSONObject json = new JSONObject();
-            for (String key : bundle.keySet()) {
-                Object value = bundle.get(key);
-                try {
-                    if (value instanceof String) {
-                        json.put(key, bundle.getString(key));
-                    } else if (value instanceof Integer) {
-                        json.put(key, bundle.getInt(key));
-                    } else if (value instanceof Long) {
-                        json.put(key, bundle.getLong(key));
-                    } else if (value instanceof Boolean) {
-                        json.put(key, bundle.getBoolean(key));
-                    } else if (value instanceof Double) {
-                        json.put(key, bundle.getDouble(key));
-                    }
-                } catch (JSONException e) {
-                    Log.e(TAG, "Unable to serialize ", e);
-                }
-            }
-            return json.toString();
-        }
-
-        private PersistableBundle jsonToPersistableBundle(String jsonString) {
-            PersistableBundle bundle = new PersistableBundle();
-            if (jsonString != null) {
-                JSONObject jsonObject = null;
-                try {
-                    jsonObject = new JSONObject(jsonString);
-
-                    Iterator<String> keys = jsonObject.keys();
-                    while (keys.hasNext()) {
-                        String key = keys.next();
-                        Object value = jsonObject.get(key);
-
-                        if (value instanceof String) {
-                            bundle.putString(key, (String) value);
-                        } else if (value instanceof Integer) {
-                            bundle.putInt(key, (Integer) value);
-                        } else if (value instanceof Boolean) {
-                            bundle.putBoolean(key, (Boolean) value);
-                        } else if (value instanceof Double) {
-                            bundle.putDouble(key, (Double) value);
-                        } else if (value instanceof Long) {
-                            bundle.putLong(key, (Long) value);
-                        }
-                    }
-                } catch (JSONException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-            return bundle;
-        }
-
-        private ContentValues getContentValues(Long dbId, Integer profileType, String name,
-                String packageName, String inputId, PersistableBundle params) {
-            ContentValues values = new ContentValues();
-            if (dbId != null) {
-                values.put(BaseParameters.PARAMETER_ID, dbId);
-            }
-            if (profileType != null) {
-                values.put(BaseParameters.PARAMETER_TYPE, profileType);
-            }
-            if (name != null) {
-                values.put(BaseParameters.PARAMETER_NAME, name);
-            }
-            if (packageName != null) {
-                values.put(BaseParameters.PARAMETER_PACKAGE, packageName);
-            }
-            if (inputId != null) {
-                values.put(BaseParameters.PARAMETER_INPUT_ID, inputId);
-            }
-            if (params != null) {
-                values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(params));
-            }
-            return values;
-        }
-
-        private String[] getMediaProfileColumns(boolean includeParams) {
-            ArrayList<String> columns = new ArrayList<>(Arrays.asList(
-                    BaseParameters.PARAMETER_ID,
-                    BaseParameters.PARAMETER_TYPE,
-                    BaseParameters.PARAMETER_NAME,
-                    BaseParameters.PARAMETER_INPUT_ID,
-                    BaseParameters.PARAMETER_PACKAGE)
-            );
-            if (includeParams) {
-                columns.add(mMediaQualityDbHelper.SETTINGS);
-            }
-            return columns.toArray(new String[0]);
-        }
-
-        private PictureProfile convertCursorToPictureProfileWithTempId(Cursor cursor) {
-            return new PictureProfile(
-                    getTempId(mPictureProfileTempIdMap, cursor),
-                    getType(cursor),
-                    getName(cursor),
-                    getInputId(cursor),
-                    getPackageName(cursor),
-                    jsonToPersistableBundle(getSettingsString(cursor)),
-                    PictureProfileHandle.NONE
-            );
-        }
-
-        private SoundProfile convertCursorToSoundProfileWithTempId(Cursor cursor) {
-            return new SoundProfile(
-                    getTempId(mSoundProfileTempIdMap, cursor),
-                    getType(cursor),
-                    getName(cursor),
-                    getInputId(cursor),
-                    getPackageName(cursor),
-                    jsonToPersistableBundle(getSettingsString(cursor)),
-                    SoundProfileHandle.NONE
-            );
-        }
-
-        private String getTempId(BiMap<Long, String> map, Cursor cursor) {
-            int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID);
-            Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null;
-            populateTempIdMap(map, dbId);
-            return map.getValue(dbId);
-        }
-
-        private int getType(Cursor cursor) {
-            int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_TYPE);
-            return colIndex != -1 ? cursor.getInt(colIndex) : 0;
-        }
-
-        private String getName(Cursor cursor) {
-            int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_NAME);
-            return colIndex != -1 ? cursor.getString(colIndex) : null;
-        }
-
-        private String getInputId(Cursor cursor) {
-            int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_INPUT_ID);
-            return colIndex != -1 ? cursor.getString(colIndex) : null;
-        }
-
-        private String getPackageName(Cursor cursor) {
-            int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_PACKAGE);
-            return colIndex != -1 ? cursor.getString(colIndex) : null;
-        }
-
-        private String getSettingsString(Cursor cursor) {
-            int colIndex = cursor.getColumnIndex(mMediaQualityDbHelper.SETTINGS);
-            return colIndex != -1 ? cursor.getString(colIndex) : null;
-        }
-
         private Cursor getCursorAfterQuerying(String table, String[] columns, String selection,
                 String[] selectionArgs) {
             SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
@@ -1048,7 +867,8 @@
             ) {
                 List<PictureProfile> pictureProfiles = new ArrayList<>();
                 while (cursor.moveToNext()) {
-                    pictureProfiles.add(convertCursorToPictureProfileWithTempId(cursor));
+                    pictureProfiles.add(MediaQualityUtils.convertCursorToPictureProfileWithTempId(
+                            cursor, mPictureProfileTempIdMap));
                 }
                 return pictureProfiles;
             }
@@ -1063,7 +883,8 @@
             ) {
                 List<SoundProfile> soundProfiles = new ArrayList<>();
                 while (cursor.moveToNext()) {
-                    soundProfiles.add(convertCursorToSoundProfileWithTempId(cursor));
+                    soundProfiles.add(MediaQualityUtils.convertCursorToSoundProfileWithTempId(
+                            cursor, mSoundProfileTempIdMap));
                 }
                 return soundProfiles;
             }
@@ -1201,6 +1022,7 @@
             userState.mSoundProfileCallbacks.finishBroadcast();
         }
 
+        //TODO: need lock here?
         @Override
         public void registerPictureProfileCallback(final IPictureProfileCallback callback) {
             int callingPid = Binder.getCallingPid();
@@ -1211,6 +1033,7 @@
                     Pair.create(callingPid, callingUid));
         }
 
+        //TODO: need lock here?
         @Override
         public void registerSoundProfileCallback(final ISoundProfileCallback callback) {
             int callingPid = Binder.getCallingPid();
@@ -1248,6 +1071,7 @@
             }
         }
 
+        @GuardedBy("mAmbientBacklightLock")
         @Override
         public void setAmbientBacklightSettings(
                 AmbientBacklightSettings settings, UserHandle user) {
@@ -1286,6 +1110,7 @@
             }
         }
 
+        @GuardedBy("mAmbientBacklightLock")
         @Override
         public void setAmbientBacklightEnabled(boolean enabled, UserHandle user) {
             if (DEBUG) {
@@ -1305,12 +1130,46 @@
             }
         }
 
+        //TODO: do I need a lock here?
         @Override
         public List<ParameterCapability> getParameterCapabilities(
                 List<String> names, UserHandle user) {
-            return new ArrayList<>();
+            byte[] byteArray = MediaQualityUtils.convertParameterToByteArray(names);
+            ParamCapability[] caps = new ParamCapability[byteArray.length];
+            try {
+                mMediaQuality.getParamCaps(byteArray, caps);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to get parameter capabilities", e);
+            }
+
+            return getListParameterCapability(caps);
         }
 
+        private List<ParameterCapability> getListParameterCapability(ParamCapability[] caps) {
+            List<ParameterCapability> pcList = new ArrayList<>();
+            for (ParamCapability pcHal : caps) {
+                String name = MediaQualityUtils.getParameterName(pcHal.name);
+                boolean isSupported = pcHal.isSupported;
+                int type = pcHal.defaultValue == null ? 0 : pcHal.defaultValue.getTag() + 1;
+                Bundle bundle = convertToCaps(pcHal.range);
+
+                pcList.add(new ParameterCapability(name, isSupported, type, bundle));
+            }
+            return pcList;
+        }
+
+        private Bundle convertToCaps(ParameterRange range) {
+            Bundle bundle = new Bundle();
+            bundle.putObject("INT_MIN_MAX", range.numRange.getIntMinMax());
+            bundle.putObject("INT_VALUES_SUPPORTED", range.numRange.getIntValuesSupported());
+            bundle.putObject("DOUBLE_MIN_MAX", range.numRange.getDoubleMinMax());
+            bundle.putObject("DOUBLE_VALUES_SUPPORTED", range.numRange.getDoubleValuesSupported());
+            bundle.putObject("LONG_MIN_MAX", range.numRange.getLongMinMax());
+            bundle.putObject("LONG_VALUES_SUPPORTED", range.numRange.getLongValuesSupported());
+            return bundle;
+        }
+
+        @GuardedBy("mPictureProfileLock")
         @Override
         public List<String> getPictureProfileAllowList(UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
@@ -1325,6 +1184,7 @@
             return new ArrayList<>();
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public void setPictureProfileAllowList(List<String> packages, UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
@@ -1336,6 +1196,7 @@
             editor.commit();
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public List<String> getSoundProfileAllowList(UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
@@ -1350,6 +1211,7 @@
             return new ArrayList<>();
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public void setSoundProfileAllowList(List<String> packages, UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
@@ -1366,70 +1228,81 @@
             return false;
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
                 notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
-
-            try {
-                if (mMediaQuality != null) {
-                    if (mMediaQuality.isAutoPqSupported()) {
-                        mMediaQuality.setAutoPqEnabled(enabled);
+            synchronized (mPictureProfileLock) {
+                try {
+                    if (mMediaQuality != null) {
+                        if (mMediaQuality.isAutoPqSupported()) {
+                            mMediaQuality.setAutoPqEnabled(enabled);
+                        }
                     }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to set auto picture quality", e);
                 }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to set auto picture quality", e);
             }
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public boolean isAutoPictureQualityEnabled(UserHandle user) {
-            try {
-                if (mMediaQuality != null) {
-                    if (mMediaQuality.isAutoPqSupported()) {
-                        return mMediaQuality.getAutoPqEnabled();
+            synchronized (mPictureProfileLock) {
+                try {
+                    if (mMediaQuality != null) {
+                        if (mMediaQuality.isAutoPqSupported()) {
+                            return mMediaQuality.getAutoPqEnabled();
+                        }
                     }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to get auto picture quality", e);
                 }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to get auto picture quality", e);
+                return false;
             }
-            return false;
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public void setSuperResolutionEnabled(boolean enabled, UserHandle user) {
             if (!hasGlobalPictureQualityServicePermission()) {
                 notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
-
-            try {
-                if (mMediaQuality != null) {
-                    if (mMediaQuality.isAutoSrSupported()) {
-                        mMediaQuality.setAutoSrEnabled(enabled);
+            synchronized (mPictureProfileLock) {
+                try {
+                    if (mMediaQuality != null) {
+                        if (mMediaQuality.isAutoSrSupported()) {
+                            mMediaQuality.setAutoSrEnabled(enabled);
+                        }
                     }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to set super resolution", e);
                 }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to set super resolution", e);
             }
         }
 
+        @GuardedBy("mPictureProfileLock")
         @Override
         public boolean isSuperResolutionEnabled(UserHandle user) {
-            try {
-                if (mMediaQuality != null) {
-                    if (mMediaQuality.isAutoSrSupported()) {
-                        return mMediaQuality.getAutoSrEnabled();
+            synchronized (mPictureProfileLock) {
+                try {
+                    if (mMediaQuality != null) {
+                        if (mMediaQuality.isAutoSrSupported()) {
+                            return mMediaQuality.getAutoSrEnabled();
+                        }
                     }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to get super resolution", e);
                 }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to get super resolution", e);
+                return false;
             }
-            return false;
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) {
             if (!hasGlobalSoundQualityServicePermission()) {
@@ -1437,31 +1310,37 @@
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
-            try {
-                if (mMediaQuality != null) {
-                    if (mMediaQuality.isAutoAqSupported()) {
-                        mMediaQuality.setAutoAqEnabled(enabled);
+            synchronized (mSoundProfileLock) {
+                try {
+                    if (mMediaQuality != null) {
+                        if (mMediaQuality.isAutoAqSupported()) {
+                            mMediaQuality.setAutoAqEnabled(enabled);
+                        }
                     }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to set auto sound quality", e);
                 }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to set auto sound quality", e);
             }
         }
 
+        @GuardedBy("mSoundProfileLock")
         @Override
         public boolean isAutoSoundQualityEnabled(UserHandle user) {
-            try {
-                if (mMediaQuality != null) {
-                    if (mMediaQuality.isAutoAqSupported()) {
-                        return mMediaQuality.getAutoAqEnabled();
+            synchronized (mSoundProfileLock) {
+                try {
+                    if (mMediaQuality != null) {
+                        if (mMediaQuality.isAutoAqSupported()) {
+                            return mMediaQuality.getAutoAqEnabled();
+                        }
                     }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to get auto sound quality", e);
                 }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to get auto sound quality", e);
+                return false;
             }
-            return false;
         }
 
+        @GuardedBy("mAmbientBacklightLock")
         @Override
         public boolean isAmbientBacklightEnabled(UserHandle user) {
             return false;
@@ -1472,7 +1351,13 @@
             RemoteCallbackList<IPictureProfileCallback> {
         @Override
         public void onCallbackDied(IPictureProfileCallback callback) {
-            //todo
+            synchronized ("mPictureProfileLock") {    //TODO: Change to lock
+                for (int i = 0; i < mUserStates.size(); i++) {
+                    int userId = mUserStates.keyAt(i);
+                    UserState userState = getOrCreateUserStateLocked(userId);
+                    userState.mPictureProfileCallbackPidUidMap.remove(callback);
+                }
+            }
         }
     }
 
@@ -1480,7 +1365,13 @@
             RemoteCallbackList<ISoundProfileCallback> {
         @Override
         public void onCallbackDied(ISoundProfileCallback callback) {
-            //todo
+            synchronized ("mSoundProfileLock") {    //TODO: Change to lock
+                for (int i = 0; i < mUserStates.size(); i++) {
+                    int userId = mUserStates.keyAt(i);
+                    UserState userState = getOrCreateUserStateLocked(userId);
+                    userState.mSoundProfileCallbackPidUidMap.remove(callback);
+                }
+            }
         }
     }
 
@@ -1503,6 +1394,7 @@
         }
     }
 
+    //TODO: used by both picture and sound. can i add both locks?
     private UserState getOrCreateUserStateLocked(int userId) {
         UserState userState = getUserStateLocked(userId);
         if (userState == null) {
@@ -1512,6 +1404,7 @@
         return userState;
     }
 
+    //TODO: used by both picture and sound. can i add both locks?
     private UserState getUserStateLocked(int userId) {
         return mUserStates.get(userId);
     }
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
new file mode 100644
index 0000000..5bd4420
--- /dev/null
+++ b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
@@ -0,0 +1,1543 @@
+/*
+ * Copyright (C) 2025 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.media.quality;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.hardware.tv.mediaquality.DolbyAudioProcessing;
+import android.hardware.tv.mediaquality.DtsVirtualX;
+import android.hardware.tv.mediaquality.ParameterName;
+import android.hardware.tv.mediaquality.PictureParameter;
+import android.hardware.tv.mediaquality.SoundParameter;
+import android.media.quality.MediaQualityContract.BaseParameters;
+import android.media.quality.MediaQualityContract.PictureQuality;
+import android.media.quality.MediaQualityContract.SoundQuality;
+import android.media.quality.PictureProfile;
+import android.media.quality.PictureProfileHandle;
+import android.media.quality.SoundProfile;
+import android.media.quality.SoundProfileHandle;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Utility class for media quality framework.
+ *
+ * @hide
+ */
+public final class MediaQualityUtils {
+
+    private static final int MAX_UUID_GENERATION_ATTEMPTS = 10;
+    private static final String TAG = "MediaQualityUtils";
+    public static final String SETTINGS = "settings";
+
+    /**
+     * Convert PictureParameter List to PersistableBundle.
+     */
+    public static PersistableBundle convertPictureParameterListToPersistableBundle(
+            PictureParameter[] parameters) {
+        PersistableBundle bundle = new PersistableBundle();
+        for (PictureParameter pp : parameters) {
+            if (pp.getBrightness() > -1) {
+                bundle.putLong(PictureQuality.PARAMETER_BRIGHTNESS, (long) pp.getBrightness());
+            }
+            if (pp.getContrast() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_CONTRAST, pp.getContrast());
+            }
+            if (pp.getSharpness() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_SHARPNESS, pp.getSharpness());
+            }
+            if (pp.getSaturation() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_SATURATION, pp.getSaturation());
+            }
+            if (pp.getHue() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_HUE, pp.getHue());
+            }
+            if (pp.getColorTunerBrightness() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS,
+                        pp.getColorTunerBrightness());
+            }
+            if (pp.getColorTunerSaturation() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION,
+                        pp.getColorTunerSaturation());
+            }
+            if (pp.getColorTunerHue() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE, pp.getColorTunerHue());
+            }
+            if (pp.getColorTunerRedOffset() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET,
+                        pp.getColorTunerRedOffset());
+            }
+            if (pp.getColorTunerGreenOffset() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET,
+                        pp.getColorTunerGreenOffset());
+            }
+            if (pp.getColorTunerBlueOffset() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET,
+                        pp.getColorTunerBlueOffset());
+            }
+            if (pp.getColorTunerRedGain() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN,
+                        pp.getColorTunerRedGain());
+            }
+            if (pp.getColorTunerGreenGain() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN,
+                        pp.getColorTunerGreenGain());
+            }
+            if (pp.getColorTunerBlueGain() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN,
+                        pp.getColorTunerBlueGain());
+            }
+            if (pp.getNoiseReduction() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_NOISE_REDUCTION,
+                        pp.getNoiseReduction());
+            }
+            if (pp.getMpegNoiseReduction() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION,
+                        pp.getMpegNoiseReduction());
+            }
+            if (pp.getFleshTone() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_FLESH_TONE, pp.getFleshTone());
+            }
+            if (pp.getDeContour() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_DECONTOUR, pp.getDeContour());
+            }
+            if (pp.getDynamicLumaControl() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL,
+                        pp.getDynamicLumaControl());
+            }
+            if (pp.getColorTemperature() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TEMPERATURE,
+                        pp.getColorTemperature());
+            }
+            if (pp.getColorTemperatureRedGain() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN,
+                        pp.getColorTemperatureRedGain());
+            }
+            if (pp.getColorTemperatureGreenGain() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN,
+                        pp.getColorTemperatureGreenGain());
+            }
+            if (pp.getColorTemperatureBlueGain() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN,
+                        pp.getColorTemperatureBlueGain());
+            }
+            if (pp.getLevelRange() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_LEVEL_RANGE, pp.getLevelRange());
+            }
+            if (pp.getHdmiRgbRange() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_HDMI_RGB_RANGE, pp.getHdmiRgbRange());
+            }
+            if (pp.getColorSpace() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_SPACE, pp.getColorSpace());
+            }
+            if (pp.getPanelInitMaxLuminceNits() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS,
+                        pp.getPanelInitMaxLuminceNits());
+            }
+            if (pp.getGamma() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_GAMMA, pp.getGamma());
+            }
+            if (pp.getColorTemperatureRedOffset() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET,
+                        pp.getColorTemperatureRedOffset());
+            }
+            if (pp.getColorTemperatureGreenOffset() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET,
+                        pp.getColorTemperatureGreenOffset());
+            }
+            if (pp.getColorTemperatureBlueOffset() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET,
+                        pp.getColorTemperatureBlueOffset());
+            }
+            if (pp.getLowBlueLight() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_LOW_BLUE_LIGHT, pp.getLowBlueLight());
+            }
+            if (pp.getLdMode() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_LD_MODE, pp.getLdMode());
+            }
+            if (pp.getOsdRedGain() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_OSD_RED_GAIN, pp.getOsdRedGain());
+            }
+            if (pp.getOsdGreenGain() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_OSD_GREEN_GAIN, pp.getOsdGreenGain());
+            }
+            if (pp.getOsdBlueGain() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_OSD_BLUE_GAIN, pp.getOsdBlueGain());
+            }
+            if (pp.getOsdRedOffset() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_OSD_RED_OFFSET, pp.getOsdRedOffset());
+            }
+            if (pp.getOsdGreenOffset() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_OSD_GREEN_OFFSET,
+                        pp.getOsdGreenOffset());
+            }
+            if (pp.getOsdBlueOffset() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_OSD_BLUE_OFFSET, pp.getOsdBlueOffset());
+            }
+            if (pp.getOsdHue() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_OSD_HUE, pp.getOsdHue());
+            }
+            if (pp.getOsdSaturation() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_OSD_SATURATION, pp.getOsdSaturation());
+            }
+            if (pp.getOsdContrast() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_OSD_CONTRAST, pp.getOsdContrast());
+            }
+            if (pp.getColorTunerHueRed() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED,
+                        pp.getColorTunerHueRed());
+            }
+            if (pp.getColorTunerHueGreen() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN,
+                        pp.getColorTunerHueGreen());
+            }
+            if (pp.getColorTunerHueBlue() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE,
+                        pp.getColorTunerHueBlue());
+            }
+            if (pp.getColorTunerHueCyan() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN,
+                        pp.getColorTunerHueCyan());
+            }
+            if (pp.getColorTunerHueMagenta() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA,
+                        pp.getColorTunerHueMagenta());
+            }
+            if (pp.getColorTunerHueYellow() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW,
+                        pp.getColorTunerHueYellow());
+            }
+            if (pp.getColorTunerHueFlesh() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH,
+                        pp.getColorTunerHueFlesh());
+            }
+            if (pp.getColorTunerSaturationRed() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED,
+                        pp.getColorTunerSaturationRed());
+            }
+            if (pp.getColorTunerSaturationGreen() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN,
+                        pp.getColorTunerSaturationGreen());
+            }
+            if (pp.getColorTunerSaturationBlue() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE,
+                        pp.getColorTunerSaturationBlue());
+            }
+            if (pp.getColorTunerSaturationCyan() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN,
+                        pp.getColorTunerSaturationCyan());
+            }
+            if (pp.getColorTunerSaturationMagenta() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA,
+                        pp.getColorTunerSaturationMagenta());
+            }
+            if (pp.getColorTunerSaturationYellow() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW,
+                        pp.getColorTunerSaturationYellow());
+            }
+            if (pp.getColorTunerSaturationFlesh() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH,
+                        pp.getColorTunerSaturationFlesh());
+            }
+            if (pp.getColorTunerLuminanceRed() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED,
+                        pp.getColorTunerLuminanceRed());
+            }
+            if (pp.getColorTunerLuminanceGreen() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN,
+                        pp.getColorTunerLuminanceGreen());
+            }
+            if (pp.getColorTunerLuminanceBlue() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE,
+                        pp.getColorTunerLuminanceBlue());
+            }
+            if (pp.getColorTunerLuminanceCyan() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN,
+                        pp.getColorTunerLuminanceCyan());
+            }
+            if (pp.getColorTunerLuminanceMagenta() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA,
+                        pp.getColorTunerLuminanceMagenta());
+            }
+            if (pp.getColorTunerLuminanceYellow() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW,
+                        pp.getColorTunerLuminanceYellow());
+            }
+            if (pp.getColorTunerLuminanceFlesh() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH,
+                        pp.getColorTunerLuminanceFlesh());
+            }
+            if (pp.getPictureQualityEventType() > -1) {
+                bundle.putInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE,
+                        pp.getPictureQualityEventType());
+            }
+            if (pp.getFilmMode()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_FILM_MODE, true);
+            }
+            if (pp.getBlueStretch()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_BLUE_STRETCH, true);
+            }
+            if (pp.getColorTune()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_COLOR_TUNE, true);
+            }
+            if (pp.getGlobeDimming()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_GLOBAL_DIMMING, true);
+            }
+            if (pp.getAutoPictureQualityEnabled()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED, true);
+            }
+            if (pp.getAutoSuperResolutionEnabled()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED, true);
+            }
+            if (pp.getGamutMapping()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_GAMUT_MAPPING, true);
+            }
+            if (pp.getPcMode()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_PC_MODE, true);
+            }
+            if (pp.getLowLatency()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_LOW_LATENCY, true);
+            }
+            if (pp.getVrr()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_VRR, true);
+            }
+            if (pp.getCvrr()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_CVRR, true);
+            }
+            if (pp.getPanelInitMaxLuminceValid()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID, true);
+            }
+            if (pp.getColorTunerSwitch()) {
+                bundle.putBoolean(PictureQuality.PARAMETER_COLOR_TUNER_SWITCH, true);
+            }
+            if (pp.getElevenPointRed() != null) {
+                bundle.putIntArray(PictureQuality.PARAMETER_ELEVEN_POINT_RED,
+                        pp.getElevenPointRed());
+            }
+            if (pp.getElevenPointBlue() != null) {
+                bundle.putIntArray(PictureQuality.PARAMETER_ELEVEN_POINT_RED,
+                        pp.getElevenPointBlue());
+            }
+            if (pp.getElevenPointGreen() != null) {
+                bundle.putIntArray(PictureQuality.PARAMETER_ELEVEN_POINT_RED,
+                        pp.getElevenPointGreen());
+            }
+        }
+        return bundle;
+    }
+
+    /**
+     * Convert PersistableBundle to PictureParameter List.
+     */
+    public static PictureParameter[] convertPersistableBundleToPictureParameterList(
+            PersistableBundle params) {
+        List<PictureParameter> pictureParams = new ArrayList<>();
+        if (params.containsKey(PictureQuality.PARAMETER_BRIGHTNESS)) {
+            pictureParams.add(PictureParameter.brightness(params.getLong(
+                    PictureQuality.PARAMETER_BRIGHTNESS)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_CONTRAST)) {
+            pictureParams.add(PictureParameter.contrast(params.getInt(
+                    PictureQuality.PARAMETER_CONTRAST)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_SHARPNESS)) {
+            pictureParams.add(PictureParameter.sharpness(params.getInt(
+                    PictureQuality.PARAMETER_SHARPNESS)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_SATURATION)) {
+            pictureParams.add(PictureParameter.saturation(params.getInt(
+                    PictureQuality.PARAMETER_SATURATION)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_HUE)) {
+            pictureParams.add(PictureParameter.hue(params.getInt(
+                    PictureQuality.PARAMETER_HUE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)) {
+            pictureParams.add(PictureParameter.colorTunerBrightness(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)) {
+            pictureParams.add(PictureParameter.colorTunerSaturation(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE)) {
+            pictureParams.add(PictureParameter.colorTunerHue(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_HUE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)) {
+            pictureParams.add(PictureParameter.colorTunerRedOffset(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)) {
+            pictureParams.add(PictureParameter.colorTunerGreenOffset(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)) {
+            pictureParams.add(PictureParameter.colorTunerBlueOffset(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
+            pictureParams.add(PictureParameter.colorTunerRedGain(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
+            pictureParams.add(PictureParameter.colorTunerGreenGain(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
+            pictureParams.add(PictureParameter.colorTunerBlueGain(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_NOISE_REDUCTION)) {
+            pictureParams.add(PictureParameter.noiseReduction(
+                    (byte) params.getInt(PictureQuality.PARAMETER_NOISE_REDUCTION)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) {
+            pictureParams.add(PictureParameter.mpegNoiseReduction(
+                    (byte) params.getInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_FLESH_TONE)) {
+            pictureParams.add(PictureParameter.fleshTone(
+                    (byte) params.getInt(PictureQuality.PARAMETER_FLESH_TONE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_DECONTOUR)) {
+            pictureParams.add(PictureParameter.deContour(
+                    (byte) params.getInt(PictureQuality.PARAMETER_DECONTOUR)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) {
+            pictureParams.add(PictureParameter.dynamicLumaControl(
+                    (byte) params.getInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_FILM_MODE)) {
+            pictureParams.add(PictureParameter.filmMode(params.getBoolean(
+                    PictureQuality.PARAMETER_FILM_MODE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_BLUE_STRETCH)) {
+            pictureParams.add(PictureParameter.blueStretch(params.getBoolean(
+                    PictureQuality.PARAMETER_BLUE_STRETCH)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNE)) {
+            pictureParams.add(PictureParameter.colorTune(params.getBoolean(
+                    PictureQuality.PARAMETER_COLOR_TUNE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) {
+            pictureParams.add(PictureParameter.colorTemperature(
+                    (byte) params.getInt(
+                            PictureQuality.PARAMETER_COLOR_TEMPERATURE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_GLOBAL_DIMMING)) {
+            pictureParams.add(PictureParameter.globeDimming(params.getBoolean(
+                    PictureQuality.PARAMETER_GLOBAL_DIMMING)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)) {
+            pictureParams.add(PictureParameter.autoPictureQualityEnabled(params.getBoolean(
+                    PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)) {
+            pictureParams.add(PictureParameter.autoSuperResolutionEnabled(params.getBoolean(
+                    PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
+            pictureParams.add(PictureParameter.colorTemperatureRedGain(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
+            pictureParams.add(PictureParameter.colorTemperatureGreenGain(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
+            pictureParams.add(PictureParameter.colorTemperatureBlueGain(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_LEVEL_RANGE)) {
+            pictureParams.add(PictureParameter.levelRange(
+                    (byte) params.getInt(PictureQuality.PARAMETER_LEVEL_RANGE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_GAMUT_MAPPING)) {
+            pictureParams.add(PictureParameter.gamutMapping(params.getBoolean(
+                    PictureQuality.PARAMETER_GAMUT_MAPPING)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_PC_MODE)) {
+            pictureParams.add(PictureParameter.pcMode(params.getBoolean(
+                    PictureQuality.PARAMETER_PC_MODE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_LOW_LATENCY)) {
+            pictureParams.add(PictureParameter.lowLatency(params.getBoolean(
+                    PictureQuality.PARAMETER_LOW_LATENCY)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_VRR)) {
+            pictureParams.add(PictureParameter.vrr(params.getBoolean(
+                    PictureQuality.PARAMETER_VRR)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_CVRR)) {
+            pictureParams.add(PictureParameter.cvrr(params.getBoolean(
+                    PictureQuality.PARAMETER_CVRR)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_HDMI_RGB_RANGE)) {
+            pictureParams.add(PictureParameter.hdmiRgbRange(
+                    (byte) params.getInt(PictureQuality.PARAMETER_HDMI_RGB_RANGE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_SPACE)) {
+            pictureParams.add(PictureParameter.colorSpace(
+                    (byte) params.getInt(PictureQuality.PARAMETER_COLOR_SPACE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)) {
+            pictureParams.add(PictureParameter.panelInitMaxLuminceNits(
+                    params.getInt(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)) {
+            pictureParams.add(PictureParameter.panelInitMaxLuminceValid(
+                    params.getBoolean(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_GAMMA)) {
+            pictureParams.add(PictureParameter.gamma(
+                    (byte) params.getInt(PictureQuality.PARAMETER_GAMMA)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) {
+            pictureParams.add(PictureParameter.colorTemperatureRedOffset(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)) {
+            pictureParams.add(PictureParameter.colorTemperatureGreenOffset(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)) {
+            pictureParams.add(PictureParameter.colorTemperatureBlueOffset(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_RED)) {
+            pictureParams.add(PictureParameter.elevenPointRed(params.getIntArray(
+                    PictureQuality.PARAMETER_ELEVEN_POINT_RED)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)) {
+            pictureParams.add(PictureParameter.elevenPointGreen(params.getIntArray(
+                    PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)) {
+            pictureParams.add(PictureParameter.elevenPointBlue(params.getIntArray(
+                    PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)) {
+            pictureParams.add(PictureParameter.lowBlueLight(
+                    (byte) params.getInt(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_LD_MODE)) {
+            pictureParams.add(PictureParameter.LdMode(
+                    (byte) params.getInt(PictureQuality.PARAMETER_LD_MODE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_GAIN)) {
+            pictureParams.add(PictureParameter.osdRedGain(params.getInt(
+                    PictureQuality.PARAMETER_OSD_RED_GAIN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_OSD_GREEN_GAIN)) {
+            pictureParams.add(PictureParameter.osdGreenGain(params.getInt(
+                    PictureQuality.PARAMETER_OSD_GREEN_GAIN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_OSD_BLUE_GAIN)) {
+            pictureParams.add(PictureParameter.osdBlueGain(params.getInt(
+                    PictureQuality.PARAMETER_OSD_BLUE_GAIN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_OFFSET)) {
+            pictureParams.add(PictureParameter.osdRedOffset(params.getInt(
+                    PictureQuality.PARAMETER_OSD_RED_OFFSET)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_OSD_GREEN_OFFSET)) {
+            pictureParams.add(PictureParameter.osdGreenOffset(params.getInt(
+                    PictureQuality.PARAMETER_OSD_GREEN_OFFSET)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_OSD_BLUE_OFFSET)) {
+            pictureParams.add(PictureParameter.osdBlueOffset(params.getInt(
+                    PictureQuality.PARAMETER_OSD_BLUE_OFFSET)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_OSD_HUE)) {
+            pictureParams.add(PictureParameter.osdHue(params.getInt(
+                    PictureQuality.PARAMETER_OSD_HUE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_OSD_SATURATION)) {
+            pictureParams.add(PictureParameter.osdSaturation(params.getInt(
+                    PictureQuality.PARAMETER_OSD_SATURATION)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_OSD_CONTRAST)) {
+            pictureParams.add(PictureParameter.osdContrast(params.getInt(
+                    PictureQuality.PARAMETER_OSD_CONTRAST)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)) {
+            pictureParams.add(PictureParameter.colorTunerSwitch(params.getBoolean(
+                    PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)) {
+            pictureParams.add(PictureParameter.colorTunerHueRed(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)) {
+            pictureParams.add(PictureParameter.colorTunerHueGreen(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)) {
+            pictureParams.add(PictureParameter.colorTunerHueBlue(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)) {
+            pictureParams.add(PictureParameter.colorTunerHueCyan(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)) {
+            pictureParams.add(PictureParameter.colorTunerHueMagenta(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)) {
+            pictureParams.add(PictureParameter.colorTunerHueYellow(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)) {
+            pictureParams.add(PictureParameter.colorTunerHueFlesh(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)) {
+            pictureParams.add(PictureParameter.colorTunerSaturationRed(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)) {
+            pictureParams.add(PictureParameter.colorTunerSaturationGreen(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)) {
+            pictureParams.add(PictureParameter.colorTunerSaturationBlue(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)) {
+            pictureParams.add(PictureParameter.colorTunerSaturationCyan(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)) {
+            pictureParams.add(PictureParameter.colorTunerSaturationMagenta(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)) {
+            pictureParams.add(PictureParameter.colorTunerSaturationYellow(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)) {
+            pictureParams.add(PictureParameter.colorTunerSaturationFlesh(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)) {
+            pictureParams.add(PictureParameter.colorTunerLuminanceRed(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)) {
+            pictureParams.add(PictureParameter.colorTunerLuminanceGreen(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)) {
+            pictureParams.add(PictureParameter.colorTunerLuminanceBlue(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)) {
+            pictureParams.add(PictureParameter.colorTunerLuminanceCyan(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)) {
+            pictureParams.add(PictureParameter.colorTunerLuminanceMagenta(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)) {
+            pictureParams.add(PictureParameter.colorTunerLuminanceYellow(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)) {
+            pictureParams.add(PictureParameter.colorTunerLuminanceFlesh(params.getInt(
+                    PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)));
+        }
+        if (params.containsKey(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)) {
+            pictureParams.add(PictureParameter.pictureQualityEventType(
+                    (byte) params.getInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)));
+        }
+        return  (PictureParameter[]) pictureParams.toArray();
+    }
+
+    /**
+     * Convert SoundParameter List to PersistableBundle.
+     */
+    public static PersistableBundle convertSoundParameterListToPersistableBundle(
+            SoundParameter[] parameters) {
+        if (parameters == null) {
+            return null;
+        }
+
+        PersistableBundle bundle = new PersistableBundle();
+        for (SoundParameter sp: parameters) {
+            if (sp.getSurroundSoundEnabled()) {
+                bundle.putBoolean(SoundQuality.PARAMETER_SURROUND_SOUND, true);
+            }
+            if (sp.getSpeakersEnabled()) {
+                bundle.putBoolean(SoundQuality.PARAMETER_SPEAKERS, true);
+            }
+            if (sp.getAutoVolumeControl()) {
+                bundle.putBoolean(SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL, true);
+            }
+            if (sp.getDtsDrc()) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DTS_DRC, true);
+            }
+            if (sp.getSurroundSoundEnabled()) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS, true);
+            }
+            if (sp.getEnhancedAudioReturnChannelEnabled()) {
+                bundle.putBoolean(SoundQuality.PARAMETER_EARC, true);
+            }
+            if (sp.getBalance() > -1) {
+                bundle.putInt(SoundQuality.PARAMETER_BALANCE, sp.getBalance());
+            }
+            if (sp.getBass() > -1) {
+                bundle.putInt(SoundQuality.PARAMETER_BASS, sp.getBass());
+            }
+            if (sp.getTreble() > -1) {
+                bundle.putInt(SoundQuality.PARAMETER_TREBLE, sp.getTreble());
+            }
+            if (sp.getSpeakersDelayMs() > -1) {
+                bundle.putInt(SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS,
+                        sp.getSpeakersDelayMs());
+            }
+            if (sp.getDownmixMode() > -1) {
+                bundle.putInt(SoundQuality.PARAMETER_DOWN_MIX_MODE, sp.getDownmixMode());
+            }
+            if (sp.getSoundStyle() > -1) {
+                bundle.putInt(SoundQuality.PARAMETER_SOUND_STYLE, sp.getSoundStyle());
+            }
+            if (sp.getDigitalOutput() > -1) {
+                bundle.putInt(SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE,
+                        sp.getDigitalOutput());
+            }
+            if (sp.getDolbyDialogueEnhancer() > -1) {
+                bundle.putInt(SoundQuality.PARAMETER_DIALOGUE_ENHANCER,
+                        sp.getDolbyDialogueEnhancer());
+            }
+            if (sp.getDtsVirtualX().tbHdx) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TBHDX, true);
+            }
+            if (sp.getDtsVirtualX().limiter) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_LIMITER, true);
+            }
+            if (sp.getDtsVirtualX().truSurroundX) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X, true);
+            }
+            if (sp.getDtsVirtualX().truVolumeHd) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD, true);
+            }
+            if (sp.getDtsVirtualX().dialogClarity) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY, true);
+            }
+            if (sp.getDtsVirtualX().definition) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_DEFINITION, true);
+            }
+            if (sp.getDtsVirtualX().height) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_HEIGHT, true);
+            }
+            if (sp.getDolbyAudioProcessing().soundMode > -1) {
+                bundle.putInt(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE,
+                        sp.getDolbyAudioProcessing().soundMode);
+            }
+            if (sp.getDolbyAudioProcessing().volumeLeveler) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER,
+                        true);
+            }
+            if (sp.getDolbyAudioProcessing().surroundVirtualizer) {
+                bundle.putBoolean(
+                        SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER,
+                        true);
+            }
+            if (sp.getDolbyAudioProcessing().dolbyAtmos) {
+                bundle.putBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS,
+                        true);
+            }
+        }
+        return bundle;
+    }
+    /**
+     * Convert PersistableBundle to SoundParameter List.
+     */
+    public static SoundParameter[] convertPersistableBundleToSoundParameterList(
+            PersistableBundle params) {
+        //TODO: set EqualizerDetail
+        List<SoundParameter> soundParams = new ArrayList<>();
+        if (params.containsKey(SoundQuality.PARAMETER_BALANCE)) {
+            soundParams.add(SoundParameter.balance(params.getInt(
+                    SoundQuality.PARAMETER_BALANCE)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_BASS)) {
+            soundParams.add(SoundParameter.bass(params.getInt(SoundQuality.PARAMETER_BASS)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_TREBLE)) {
+            soundParams.add(SoundParameter.treble(params.getInt(
+                    SoundQuality.PARAMETER_TREBLE)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_SURROUND_SOUND)) {
+            soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
+                    SoundQuality.PARAMETER_SURROUND_SOUND)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_SPEAKERS)) {
+            soundParams.add(SoundParameter.speakersEnabled(params.getBoolean(
+                    SoundQuality.PARAMETER_SPEAKERS)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)) {
+            soundParams.add(SoundParameter.speakersDelayMs(params.getInt(
+                    SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)) {
+            soundParams.add(SoundParameter.autoVolumeControl(params.getBoolean(
+                    SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_DTS_DRC)) {
+            soundParams.add(SoundParameter.dtsDrc(params.getBoolean(
+                    SoundQuality.PARAMETER_DTS_DRC)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)) {
+            soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
+                    SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_EARC)) {
+            soundParams.add(SoundParameter.enhancedAudioReturnChannelEnabled(params.getBoolean(
+                    SoundQuality.PARAMETER_EARC)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_DOWN_MIX_MODE)) {
+            soundParams.add(SoundParameter.downmixMode((byte) params.getInt(
+                    SoundQuality.PARAMETER_DOWN_MIX_MODE)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_SOUND_STYLE)) {
+            soundParams.add(SoundParameter.soundStyle((byte) params.getInt(
+                    SoundQuality.PARAMETER_SOUND_STYLE)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE)) {
+            soundParams.add(SoundParameter.digitalOutput((byte) params.getInt(
+                    SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE)));
+        }
+        if (params.containsKey(SoundQuality.PARAMETER_DIALOGUE_ENHANCER)) {
+            soundParams.add(SoundParameter.dolbyDialogueEnhancer((byte) params.getInt(
+                    SoundQuality.PARAMETER_DIALOGUE_ENHANCER)));
+        }
+
+        DolbyAudioProcessing dab = new DolbyAudioProcessing();
+        dab.soundMode =
+                (byte) params.getInt(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE);
+        dab.volumeLeveler =
+                params.getBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER);
+        dab.surroundVirtualizer = params.getBoolean(
+                SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER);
+        dab.dolbyAtmos =
+                params.getBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS);
+        soundParams.add(SoundParameter.dolbyAudioProcessing(dab));
+
+        DtsVirtualX dts = new DtsVirtualX();
+        dts.tbHdx = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TBHDX);
+        dts.limiter = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_LIMITER);
+        dts.truSurroundX = params.getBoolean(
+                SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X);
+        dts.truVolumeHd = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD);
+        dts.dialogClarity = params.getBoolean(
+                SoundQuality.PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY);
+        dts.definition = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_DEFINITION);
+        dts.height = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_HEIGHT);
+        soundParams.add(SoundParameter.dtsVirtualX(dts));
+
+        return  (SoundParameter[]) soundParams.toArray();
+    }
+
+    private static String persistableBundleToJson(PersistableBundle bundle) {
+        JSONObject json = new JSONObject();
+        for (String key : bundle.keySet()) {
+            Object value = bundle.get(key);
+            try {
+                if (value instanceof String) {
+                    json.put(key, bundle.getString(key));
+                } else if (value instanceof Integer) {
+                    json.put(key, bundle.getInt(key));
+                } else if (value instanceof Long) {
+                    json.put(key, bundle.getLong(key));
+                } else if (value instanceof Boolean) {
+                    json.put(key, bundle.getBoolean(key));
+                } else if (value instanceof Double) {
+                    json.put(key, bundle.getDouble(key));
+                }
+            } catch (JSONException e) {
+                Log.e(TAG, "Unable to serialize ", e);
+            }
+        }
+        return json.toString();
+    }
+
+    private static PersistableBundle jsonToPersistableBundle(String jsonString) {
+        PersistableBundle bundle = new PersistableBundle();
+        if (jsonString != null) {
+            JSONObject jsonObject = null;
+            try {
+                jsonObject = new JSONObject(jsonString);
+
+                Iterator<String> keys = jsonObject.keys();
+                while (keys.hasNext()) {
+                    String key = keys.next();
+                    Object value = jsonObject.get(key);
+
+                    if (value instanceof String) {
+                        bundle.putString(key, (String) value);
+                    } else if (value instanceof Integer) {
+                        bundle.putInt(key, (Integer) value);
+                    } else if (value instanceof Boolean) {
+                        bundle.putBoolean(key, (Boolean) value);
+                    } else if (value instanceof Double) {
+                        bundle.putDouble(key, (Double) value);
+                    } else if (value instanceof Long) {
+                        bundle.putLong(key, (Long) value);
+                    }
+                }
+            } catch (JSONException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return bundle;
+    }
+
+    /**
+     * Populates the given map with the ID and generated UUID.
+     */
+    public static void populateTempIdMap(BiMap<Long, String> map, Long id) {
+        if (id != null && map.getValue(id) == null) {
+            String uuid;
+            int attempts = 0;
+            while (attempts < MAX_UUID_GENERATION_ATTEMPTS) {
+                uuid = UUID.randomUUID().toString();
+                if (map.getKey(uuid) == null) {
+                    map.put(id, uuid);
+                    return;
+                }
+                attempts++;
+            }
+        }
+    }
+
+    /**
+     * Get Content Values.
+     */
+    public static ContentValues getContentValues(Long dbId, Integer profileType, String name,
+            String packageName, String inputId, PersistableBundle params) {
+        ContentValues values = new ContentValues();
+        if (dbId != null) {
+            values.put(BaseParameters.PARAMETER_ID, dbId);
+        }
+        if (profileType != null) {
+            values.put(BaseParameters.PARAMETER_TYPE, profileType);
+        }
+        if (name != null) {
+            values.put(BaseParameters.PARAMETER_NAME, name);
+        }
+        if (packageName != null) {
+            values.put(BaseParameters.PARAMETER_PACKAGE, packageName);
+        }
+        if (inputId != null) {
+            values.put(BaseParameters.PARAMETER_INPUT_ID, inputId);
+        }
+        if (params != null) {
+            values.put(SETTINGS, persistableBundleToJson(params));
+        }
+        return values;
+    }
+
+    /**
+     * Get Media Profile Columns.
+     */
+    public static String[] getMediaProfileColumns(boolean includeParams) {
+        ArrayList<String> columns = new ArrayList<>(Arrays.asList(
+                BaseParameters.PARAMETER_ID,
+                BaseParameters.PARAMETER_TYPE,
+                BaseParameters.PARAMETER_NAME,
+                BaseParameters.PARAMETER_INPUT_ID,
+                BaseParameters.PARAMETER_PACKAGE)
+        );
+        if (includeParams) {
+            columns.add(SETTINGS);
+        }
+        return columns.toArray(new String[0]);
+    }
+
+    /**
+     * Convert cursor to Picture Profile with temporary UUID.
+     */
+    public static PictureProfile convertCursorToPictureProfileWithTempId(Cursor cursor,
+            BiMap<Long, String> map) {
+        return new PictureProfile(
+                getTempId(map, cursor),
+                getType(cursor),
+                getName(cursor),
+                getInputId(cursor),
+                getPackageName(cursor),
+                jsonToPersistableBundle(getSettingsString(cursor)),
+                PictureProfileHandle.NONE
+        );
+    }
+
+    /**
+     * Convert cursor to Sound Profile with temporary UUID.
+     */
+    public static SoundProfile convertCursorToSoundProfileWithTempId(Cursor cursor, BiMap<Long,
+            String> map) {
+        return new SoundProfile(
+                getTempId(map, cursor),
+                getType(cursor),
+                getName(cursor),
+                getInputId(cursor),
+                getPackageName(cursor),
+                jsonToPersistableBundle(getSettingsString(cursor)),
+                SoundProfileHandle.NONE
+        );
+    }
+
+    /**
+     * Convert parameter to byte array.
+     */
+    public static byte[] convertParameterToByteArray(List<String> names) {
+        /**
+         * TODO Add following to ParameterName & add conversion here.
+         * - PICTURE_QUALITY_EVENT_TYPE
+         * - PANEL_INIT_MAX_LUMINCE_NITS
+         */
+
+        HashSet<String> nameMap = new HashSet<>(names);
+
+        List<Byte> bytes = new ArrayList<>();
+        // Picture Quality parameters
+        if (nameMap.contains(PictureQuality.PARAMETER_BRIGHTNESS)) {
+            bytes.add(ParameterName.BRIGHTNESS);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_BRIGHTNESS)) {
+            bytes.add(ParameterName.BRIGHTNESS);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_CONTRAST)) {
+            bytes.add(ParameterName.CONTRAST);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_SHARPNESS)) {
+            bytes.add(ParameterName.SHARPNESS);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_SATURATION)) {
+            bytes.add(ParameterName.SATURATION);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_HUE)) {
+            bytes.add(ParameterName.HUE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_BRIGHTNESS)) {
+            bytes.add(ParameterName.BRIGHTNESS);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)) {
+            bytes.add(ParameterName.COLOR_TUNER_BRIGHTNESS);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_SATURATION)) {
+            bytes.add(ParameterName.SATURATION);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)) {
+            bytes.add(ParameterName.COLOR_TUNER_SATURATION);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_HUE)) {
+            bytes.add(ParameterName.HUE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE)) {
+            bytes.add(ParameterName.COLOR_TUNER_HUE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)) {
+            bytes.add(ParameterName.COLOR_TUNER_RED_OFFSET);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)) {
+            bytes.add(ParameterName.COLOR_TUNER_GREEN_OFFSET);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)) {
+            bytes.add(ParameterName.COLOR_TUNER_BLUE_OFFSET);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
+            bytes.add(ParameterName.COLOR_TUNER_RED_GAIN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
+            bytes.add(ParameterName.COLOR_TUNER_GREEN_GAIN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
+            bytes.add(ParameterName.COLOR_TUNER_BLUE_GAIN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_NOISE_REDUCTION)) {
+            bytes.add(ParameterName.NOISE_REDUCTION);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) {
+            bytes.add(ParameterName.MPEG_NOISE_REDUCTION);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_FLESH_TONE)) {
+            bytes.add(ParameterName.FLASH_TONE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_DECONTOUR)) {
+            bytes.add(ParameterName.DE_CONTOUR);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) {
+            bytes.add(ParameterName.DYNAMIC_LUMA_CONTROL);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_FILM_MODE)) {
+            bytes.add(ParameterName.FILM_MODE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_BLUE_STRETCH)) {
+            bytes.add(ParameterName.BLUE_STRETCH);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNE)) {
+            bytes.add(ParameterName.COLOR_TUNE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) {
+            bytes.add(ParameterName.COLOR_TEMPERATURE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_GLOBAL_DIMMING)) {
+            bytes.add(ParameterName.GLOBE_DIMMING);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)) {
+            bytes.add(ParameterName.AUTO_PICTUREQUALITY_ENABLED);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)) {
+            bytes.add(ParameterName.AUTO_SUPER_RESOLUTION_ENABLED);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_LEVEL_RANGE)) {
+            bytes.add(ParameterName.LEVEL_RANGE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_GAMUT_MAPPING)) {
+            bytes.add(ParameterName.GAMUT_MAPPING);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_PC_MODE)) {
+            bytes.add(ParameterName.PC_MODE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_LOW_LATENCY)) {
+            bytes.add(ParameterName.LOW_LATENCY);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_VRR)) {
+            bytes.add(ParameterName.VRR);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_CVRR)) {
+            bytes.add(ParameterName.CVRR);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_HDMI_RGB_RANGE)) {
+            bytes.add(ParameterName.HDMI_RGB_RANGE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_SPACE)) {
+            bytes.add(ParameterName.COLOR_SPACE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)) {
+            bytes.add(ParameterName.PANEL_INIT_MAX_LUMINCE_VALID);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_GAMMA)) {
+            bytes.add(ParameterName.GAMMA);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) {
+            bytes.add(ParameterName.COLOR_TEMPERATURE_RED_OFFSET);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)) {
+            bytes.add(ParameterName.COLOR_TEMPERATURE_GREEN_OFFSET);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)) {
+            bytes.add(ParameterName.COLOR_TEMPERATURE_BLUE_OFFSET);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_ELEVEN_POINT_RED)) {
+            bytes.add(ParameterName.ELEVEN_POINT_RED);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)) {
+            bytes.add(ParameterName.ELEVEN_POINT_GREEN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)) {
+            bytes.add(ParameterName.ELEVEN_POINT_BLUE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)) {
+            bytes.add(ParameterName.LOW_BLUE_LIGHT);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_LD_MODE)) {
+            bytes.add(ParameterName.LD_MODE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_OSD_RED_GAIN)) {
+            bytes.add(ParameterName.OSD_RED_GAIN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_OSD_GREEN_GAIN)) {
+            bytes.add(ParameterName.OSD_GREEN_GAIN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_OSD_BLUE_GAIN)) {
+            bytes.add(ParameterName.OSD_BLUE_GAIN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_OSD_RED_OFFSET)) {
+            bytes.add(ParameterName.OSD_RED_OFFSET);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_OSD_GREEN_OFFSET)) {
+            bytes.add(ParameterName.OSD_GREEN_OFFSET);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_OSD_BLUE_OFFSET)) {
+            bytes.add(ParameterName.OSD_BLUE_OFFSET);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_OSD_HUE)) {
+            bytes.add(ParameterName.OSD_HUE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_OSD_SATURATION)) {
+            bytes.add(ParameterName.OSD_SATURATION);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_OSD_CONTRAST)) {
+            bytes.add(ParameterName.OSD_CONTRAST);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)) {
+            bytes.add(ParameterName.COLOR_TUNER_SWITCH);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)) {
+            bytes.add(ParameterName.COLOR_TUNER_HUE_RED);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)) {
+            bytes.add(ParameterName.COLOR_TUNER_HUE_GREEN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)) {
+            bytes.add(ParameterName.COLOR_TUNER_HUE_BLUE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)) {
+            bytes.add(ParameterName.COLOR_TUNER_HUE_CYAN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)) {
+            bytes.add(ParameterName.COLOR_TUNER_HUE_MAGENTA);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)) {
+            bytes.add(ParameterName.COLOR_TUNER_HUE_YELLOW);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)) {
+            bytes.add(ParameterName.COLOR_TUNER_HUE_FLESH);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)) {
+            bytes.add(ParameterName.COLOR_TUNER_SATURATION_RED);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)) {
+            bytes.add(ParameterName.COLOR_TUNER_SATURATION_GREEN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)) {
+            bytes.add(ParameterName.COLOR_TUNER_SATURATION_BLUE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)) {
+            bytes.add(ParameterName.COLOR_TUNER_SATURATION_CYAN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)) {
+            bytes.add(ParameterName.COLOR_TUNER_SATURATION_MAGENTA);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)) {
+            bytes.add(ParameterName.COLOR_TUNER_SATURATION_YELLOW);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)) {
+            bytes.add(ParameterName.COLOR_TUNER_SATURATION_FLESH);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)) {
+            bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_RED);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)) {
+            bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_GREEN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)) {
+            bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_BLUE);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)) {
+            bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_CYAN);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)) {
+            bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_MAGENTA);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)) {
+            bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_YELLOW);
+        }
+        if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)) {
+            bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_FLESH);
+        }
+
+        // Sound Quality parameters
+        if (nameMap.contains(SoundQuality.PARAMETER_BALANCE)) {
+            bytes.add(ParameterName.BALANCE);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_BASS)) {
+            bytes.add(ParameterName.BASS);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_TREBLE)) {
+            bytes.add(ParameterName.TREBLE);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_SURROUND_SOUND)) {
+            bytes.add(ParameterName.SURROUND_SOUND_ENABLED);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_EQUALIZER_DETAIL)) {
+            bytes.add(ParameterName.EQUALIZER_DETAIL);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_SPEAKERS)) {
+            bytes.add(ParameterName.SPEAKERS_ENABLED);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)) {
+            bytes.add(ParameterName.SPEAKERS_DELAY_MS);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_EARC)) {
+            bytes.add(ParameterName.ENHANCED_AUDIO_RETURN_CHANNEL_ENABLED);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)) {
+            bytes.add(ParameterName.AUTO_VOLUME_CONTROL);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_DOWN_MIX_MODE)) {
+            bytes.add(ParameterName.DOWNMIX_MODE);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_DTS_DRC)) {
+            bytes.add(ParameterName.DTS_DRC);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING)) {
+            bytes.add(ParameterName.DOLBY_AUDIO_PROCESSING);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_DIALOGUE_ENHANCER)) {
+            bytes.add(ParameterName.DOLBY_DIALOGUE_ENHANCER);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_DTS_VIRTUAL_X)) {
+            bytes.add(ParameterName.DTS_VIRTUAL_X);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)) {
+            bytes.add(ParameterName.DIGITAL_OUTPUT_DELAY_MS);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE)) {
+            bytes.add(ParameterName.DIGITAL_OUTPUT);
+        }
+        if (nameMap.contains(SoundQuality.PARAMETER_SOUND_STYLE)) {
+            bytes.add(ParameterName.SOUND_STYLE);
+        }
+
+        byte[] byteArray = new byte[bytes.size()];
+        for (int i = 0; i < bytes.size(); i++) {
+            byteArray[i] = bytes.get(i);
+        }
+        return byteArray;
+    }
+
+    /**
+     * Get Parameter Name based on byte.
+     */
+    public static String getParameterName(byte pn) {
+        Map<Byte, String> parameterNameMap = new HashMap<>();
+        parameterNameMap.put(ParameterName.BRIGHTNESS, PictureQuality.PARAMETER_BRIGHTNESS);
+        parameterNameMap.put(ParameterName.CONTRAST, PictureQuality.PARAMETER_CONTRAST);
+        parameterNameMap.put(ParameterName.SHARPNESS, PictureQuality.PARAMETER_SHARPNESS);
+        parameterNameMap.put(ParameterName.SATURATION, PictureQuality.PARAMETER_SATURATION);
+        parameterNameMap.put(ParameterName.HUE, PictureQuality.PARAMETER_HUE);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_BRIGHTNESS,
+                PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION,
+                PictureQuality.PARAMETER_COLOR_TUNER_SATURATION);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_HUE,
+                PictureQuality.PARAMETER_COLOR_TUNER_HUE);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_RED_OFFSET,
+                PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_GREEN_OFFSET,
+                PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_BLUE_OFFSET,
+                PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_RED_GAIN,
+                PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_GREEN_GAIN,
+                PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_BLUE_GAIN,
+                PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN);
+        parameterNameMap.put(ParameterName.NOISE_REDUCTION,
+                PictureQuality.PARAMETER_NOISE_REDUCTION);
+        parameterNameMap.put(ParameterName.MPEG_NOISE_REDUCTION,
+                PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION);
+        parameterNameMap.put(ParameterName.FLASH_TONE, PictureQuality.PARAMETER_FLESH_TONE);
+        parameterNameMap.put(ParameterName.DE_CONTOUR, PictureQuality.PARAMETER_DECONTOUR);
+        parameterNameMap.put(ParameterName.DYNAMIC_LUMA_CONTROL,
+                PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL);
+        parameterNameMap.put(ParameterName.FILM_MODE,
+                PictureQuality.PARAMETER_FILM_MODE);
+        parameterNameMap.put(ParameterName.BLACK_STRETCH,
+                PictureQuality.PARAMETER_BLACK_STRETCH);
+        parameterNameMap.put(ParameterName.BLUE_STRETCH,
+                PictureQuality.PARAMETER_BLUE_STRETCH);
+        parameterNameMap.put(ParameterName.COLOR_TUNE,
+                PictureQuality.PARAMETER_COLOR_TUNE);
+        parameterNameMap.put(ParameterName.COLOR_TEMPERATURE,
+                PictureQuality.PARAMETER_COLOR_TEMPERATURE);
+        parameterNameMap.put(ParameterName.GLOBE_DIMMING,
+                PictureQuality.PARAMETER_GLOBAL_DIMMING);
+        parameterNameMap.put(ParameterName.AUTO_PICTUREQUALITY_ENABLED,
+                PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED);
+        parameterNameMap.put(ParameterName.AUTO_SUPER_RESOLUTION_ENABLED,
+                PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED);
+        parameterNameMap.put(ParameterName.LEVEL_RANGE, PictureQuality.PARAMETER_LEVEL_RANGE);
+        parameterNameMap.put(ParameterName.GAMUT_MAPPING,
+                PictureQuality.PARAMETER_GAMUT_MAPPING);
+        parameterNameMap.put(ParameterName.PC_MODE, PictureQuality.PARAMETER_PC_MODE);
+        parameterNameMap.put(ParameterName.LOW_LATENCY, PictureQuality.PARAMETER_LOW_LATENCY);
+        parameterNameMap.put(ParameterName.VRR, PictureQuality.PARAMETER_VRR);
+        parameterNameMap.put(ParameterName.CVRR, PictureQuality.PARAMETER_CVRR);
+        parameterNameMap.put(ParameterName.HDMI_RGB_RANGE,
+                PictureQuality.PARAMETER_HDMI_RGB_RANGE);
+        parameterNameMap.put(ParameterName.COLOR_SPACE, PictureQuality.PARAMETER_COLOR_SPACE);
+        parameterNameMap.put(ParameterName.PANEL_INIT_MAX_LUMINCE_VALID,
+                PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID);
+        parameterNameMap.put(ParameterName.GAMMA, PictureQuality.PARAMETER_GAMMA);
+        parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_RED_GAIN,
+                PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_GAIN);
+        parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_GREEN_GAIN,
+                PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_GAIN);
+        parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_BLUE_GAIN,
+                PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_GAIN);
+        parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_RED_OFFSET,
+                PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET);
+        parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_GREEN_OFFSET,
+                PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET);
+        parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_BLUE_OFFSET,
+                PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET);
+        parameterNameMap.put(ParameterName.ELEVEN_POINT_RED,
+                PictureQuality.PARAMETER_ELEVEN_POINT_RED);
+        parameterNameMap.put(ParameterName.ELEVEN_POINT_GREEN,
+                PictureQuality.PARAMETER_ELEVEN_POINT_GREEN);
+        parameterNameMap.put(ParameterName.ELEVEN_POINT_BLUE,
+                PictureQuality.PARAMETER_ELEVEN_POINT_BLUE);
+        parameterNameMap.put(ParameterName.LOW_BLUE_LIGHT,
+                PictureQuality.PARAMETER_LOW_BLUE_LIGHT);
+        parameterNameMap.put(ParameterName.LD_MODE, PictureQuality.PARAMETER_LD_MODE);
+        parameterNameMap.put(ParameterName.OSD_RED_GAIN, PictureQuality.PARAMETER_OSD_RED_GAIN);
+        parameterNameMap.put(ParameterName.OSD_GREEN_GAIN,
+                PictureQuality.PARAMETER_OSD_GREEN_GAIN);
+        parameterNameMap.put(ParameterName.OSD_BLUE_GAIN,
+                PictureQuality.PARAMETER_OSD_BLUE_GAIN);
+        parameterNameMap.put(ParameterName.OSD_RED_OFFSET,
+                PictureQuality.PARAMETER_OSD_RED_OFFSET);
+        parameterNameMap.put(ParameterName.OSD_GREEN_OFFSET,
+                PictureQuality.PARAMETER_OSD_GREEN_OFFSET);
+        parameterNameMap.put(ParameterName.OSD_BLUE_OFFSET,
+                PictureQuality.PARAMETER_OSD_BLUE_OFFSET);
+        parameterNameMap.put(ParameterName.OSD_HUE, PictureQuality.PARAMETER_OSD_HUE);
+        parameterNameMap.put(ParameterName.OSD_SATURATION,
+                PictureQuality.PARAMETER_OSD_SATURATION);
+        parameterNameMap.put(ParameterName.OSD_CONTRAST,
+                PictureQuality.PARAMETER_OSD_CONTRAST);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_SWITCH,
+                PictureQuality.PARAMETER_COLOR_TUNER_SWITCH);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_RED,
+                PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_GREEN,
+                PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_BLUE,
+                PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_CYAN,
+                PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_MAGENTA,
+                PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_YELLOW,
+                PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_FLESH,
+                PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_RED,
+                PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_GREEN,
+                PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_BLUE,
+                PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_CYAN,
+                PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_MAGENTA,
+                PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_YELLOW,
+                PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_FLESH,
+                PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_RED,
+                PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_GREEN,
+                PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_BLUE,
+                PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_CYAN,
+                PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_MAGENTA,
+                PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_YELLOW,
+                PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW);
+        parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_FLESH,
+                PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH);
+        parameterNameMap.put(ParameterName.BALANCE, SoundQuality.PARAMETER_BALANCE);
+        parameterNameMap.put(ParameterName.BASS, SoundQuality.PARAMETER_BASS);
+        parameterNameMap.put(ParameterName.TREBLE, SoundQuality.PARAMETER_TREBLE);
+        parameterNameMap.put(ParameterName.SURROUND_SOUND_ENABLED,
+                SoundQuality.PARAMETER_SURROUND_SOUND);
+        parameterNameMap.put(ParameterName.EQUALIZER_DETAIL,
+                SoundQuality.PARAMETER_EQUALIZER_DETAIL);
+        parameterNameMap.put(ParameterName.SPEAKERS_ENABLED, SoundQuality.PARAMETER_SPEAKERS);
+        parameterNameMap.put(ParameterName.SPEAKERS_DELAY_MS,
+                SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS);
+        parameterNameMap.put(ParameterName.ENHANCED_AUDIO_RETURN_CHANNEL_ENABLED,
+                SoundQuality.PARAMETER_EARC);
+        parameterNameMap.put(ParameterName.AUTO_VOLUME_CONTROL,
+                SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL);
+        parameterNameMap.put(ParameterName.DOWNMIX_MODE, SoundQuality.PARAMETER_DOWN_MIX_MODE);
+        parameterNameMap.put(ParameterName.DTS_DRC, SoundQuality.PARAMETER_DTS_DRC);
+        parameterNameMap.put(ParameterName.DOLBY_AUDIO_PROCESSING,
+                SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING);
+        parameterNameMap.put(ParameterName.DOLBY_DIALOGUE_ENHANCER,
+                SoundQuality.PARAMETER_DIALOGUE_ENHANCER);
+        parameterNameMap.put(ParameterName.DTS_VIRTUAL_X,
+                SoundQuality.PARAMETER_DTS_VIRTUAL_X);
+        parameterNameMap.put(ParameterName.DIGITAL_OUTPUT,
+                SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE);
+        parameterNameMap.put(ParameterName.DIGITAL_OUTPUT_DELAY_MS,
+                SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS);
+        parameterNameMap.put(ParameterName.SOUND_STYLE, SoundQuality.PARAMETER_SOUND_STYLE);
+
+        return parameterNameMap.get(pn);
+    }
+
+    private static String getTempId(BiMap<Long, String> map, Cursor cursor) {
+        int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID);
+        Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null;
+        populateTempIdMap(map, dbId);
+        return map.getValue(dbId);
+    }
+
+    private static int getType(Cursor cursor) {
+        int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_TYPE);
+        return colIndex != -1 ? cursor.getInt(colIndex) : 0;
+    }
+
+    private static String getName(Cursor cursor) {
+        int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_NAME);
+        return colIndex != -1 ? cursor.getString(colIndex) : null;
+    }
+
+    private static String getInputId(Cursor cursor) {
+        int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_INPUT_ID);
+        return colIndex != -1 ? cursor.getString(colIndex) : null;
+    }
+
+    private static String getPackageName(Cursor cursor) {
+        int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_PACKAGE);
+        return colIndex != -1 ? cursor.getString(colIndex) : null;
+    }
+
+    private static String getSettingsString(Cursor cursor) {
+        int colIndex = cursor.getColumnIndex(SETTINGS);
+        return colIndex != -1 ? cursor.getString(colIndex) : null;
+    }
+
+    private MediaQualityUtils() {
+
+    }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 7de2815..1d376b4 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3612,13 +3612,16 @@
         final long token = Binder.clearCallingIdentity();
         try {
             config = mCarrierConfigManager.getConfigForSubId(subId);
-            tm = mContext.getSystemService(TelephonyManager.class);
+            tm = mContext.getSystemService(TelephonyManager.class).createForSubscriptionId(subId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
 
-        // First check: does caller have carrier privilege?
-        if (tm != null && tm.hasCarrierPrivileges(subId)) {
+        // First check: does callingPackage have carrier privilege?
+        // Note that we can't call TelephonyManager.hasCarrierPrivileges() which will check if
+        // ourself has carrier privileges
+        if (tm != null && (tm.checkCarrierPrivilegesForPackage(callingPackage)
+                == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS)) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 5a42505..89902f7 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -101,15 +101,4 @@
     void onNotificationFeedbackReceived(String key, Bundle feedback);
 
     void prepareForPossibleShutdown();
-
-    /**
-     *  Called when the notification should be unbundled.
-     * @param key the notification key
-     */
-    void unbundleNotification(String key);
-    /**
-     *  Called when the notification should be rebundled.
-     * @param key the notification key
-     */
-    void rebundleNotification(String key);
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index daa1042..6d565d2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -26,6 +26,8 @@
 import static android.app.AppOpsManager.OP_RECEIVE_SENSITIVE_NOTIFICATIONS;
 import static android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR;
 import static android.app.Flags.lifetimeExtensionRefactor;
+import static android.app.Flags.nmSummarization;
+import static android.app.Flags.nmSummarizationUi;
 import static android.app.Flags.notificationClassificationUi;
 import static android.app.Flags.redactSensitiveContentNotificationsOnLockscreen;
 import static android.app.Flags.sortSectionByTime;
@@ -106,6 +108,7 @@
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_NULL;
 import static android.os.UserHandle.USER_SYSTEM;
+import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
 import static android.service.notification.Adjustment.KEY_TYPE;
 import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
 import static android.service.notification.Adjustment.TYPE_PROMOTION;
@@ -290,6 +293,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.notification.Adjustment;
+import android.service.notification.Adjustment.Types;
 import android.service.notification.Condition;
 import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.DeviceEffectsApplier;
@@ -417,6 +421,7 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 /** {@hide} */
@@ -481,7 +486,8 @@
             Adjustment.KEY_SENSITIVE_CONTENT,
             Adjustment.KEY_RANKING_SCORE,
             Adjustment.KEY_NOT_CONVERSATION,
-            Adjustment.KEY_TYPE
+            Adjustment.KEY_TYPE,
+            Adjustment.KEY_SUMMARIZATION
     };
 
     static final Integer[] DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES = new Integer[] {
@@ -631,6 +637,8 @@
     // Minium number of sparse groups for a package before autogrouping them
     private static final int AUTOGROUP_SPARSE_GROUPS_AT_COUNT = 3;
 
+    private static final Duration ZEN_BROADCAST_DELAY = Duration.ofMillis(250);
+
     private IActivityManager mAm;
     private ActivityTaskManagerInternal mAtm;
     private ActivityManager mActivityManager;
@@ -671,6 +679,8 @@
     WorkerHandler mHandler;
     private final HandlerThread mRankingThread = new HandlerThread("ranker",
             Process.THREAD_PRIORITY_BACKGROUND);
+    @FlaggedApi(Flags.FLAG_NM_BINDER_PERF_THROTTLE_EFFECTS_SUPPRESSOR_BROADCAST)
+    private Handler mBroadcastsHandler;
 
     private final SparseArray<ArraySet<ComponentName>> mListenersDisablingEffects =
             new SparseArray<>();
@@ -1862,75 +1872,184 @@
                 mAssistants.notifyAssistantFeedbackReceived(r, feedback);
             }
         }
-
-        @Override
-        public void unbundleNotification(String key) {
-            if (!(notificationClassification() && notificationRegroupOnClassification())) {
-                return;
-            }
-            synchronized (mNotificationLock) {
-                NotificationRecord r = mNotificationsByKey.get(key);
-                if (r == null) {
-                    return;
-                }
-
-                if (DBG) {
-                    Slog.v(TAG, "unbundleNotification: " + r);
-                }
-
-                boolean hasOriginalSummary = false;
-                if (r.getSbn().isAppGroup() && r.getNotification().isGroupChild()) {
-                    final String oldGroupKey = GroupHelper.getFullAggregateGroupKey(
-                            r.getSbn().getPackageName(), r.getOriginalGroupKey(), r.getUserId());
-                    NotificationRecord groupSummary = mSummaryByGroupKey.get(oldGroupKey);
-                    // We only care about app-provided valid groups
-                    hasOriginalSummary = (groupSummary != null
-                            && !GroupHelper.isAggregatedGroup(groupSummary));
-                }
-
-                // Only NotificationRecord's mChannel is updated when bundled, the Notification
-                // mChannelId will always be the original channel.
-                String origChannelId = r.getNotification().getChannelId();
-                NotificationChannel originalChannel = mPreferencesHelper.getNotificationChannel(
-                        r.getSbn().getPackageName(), r.getUid(), origChannelId, false);
-                if (originalChannel != null && !origChannelId.equals(r.getChannel().getId())) {
-                    r.updateNotificationChannel(originalChannel);
-                    mGroupHelper.onNotificationUnbundled(r, hasOriginalSummary);
-                }
-            }
-        }
-
-        @Override
-        public void rebundleNotification(String key) {
-            if (!(notificationClassification() && notificationRegroupOnClassification())) {
-                return;
-            }
-            synchronized (mNotificationLock) {
-                NotificationRecord r = mNotificationsByKey.get(key);
-                if (r == null) {
-                    return;
-                }
-
-                if (DBG) {
-                    Slog.v(TAG, "rebundleNotification: " + r);
-                }
-
-                if (r.getBundleType() != Adjustment.TYPE_OTHER) {
-                    final Bundle classifBundle = new Bundle();
-                    classifBundle.putInt(KEY_TYPE, r.getBundleType());
-                    Adjustment adj = new Adjustment(r.getSbn().getPackageName(), r.getKey(),
-                            classifBundle, "rebundle", r.getUserId());
-                    applyAdjustmentLocked(r, adj, /* isPosted= */ true);
-                    mRankingHandler.requestSort();
-                } else {
-                    if (DBG) {
-                        Slog.w(TAG, "Can't rebundle. No valid bundle type for: " + r);
-                    }
-                }
-            }
-        }
     };
 
+    private void unclassifyNotificationsForUser(final int userId) {
+        if (DBG) {
+            Slog.v(TAG, "unclassifyForUser: " + userId);
+        }
+        unclassifyNotificationsFiltered((r) -> r.getUserId() == userId);
+    }
+
+    private void unclassifyNotificationsForUid(final int userId, @NonNull final String pkg) {
+        if (DBG) {
+            Slog.v(TAG, "unclassifyForUid userId: " + userId + " pkg: " + pkg);
+        }
+        unclassifyNotificationsFiltered((r) ->
+                r.getUserId() == userId
+                && Objects.equals(r.getSbn().getPackageName(), pkg));
+    }
+
+    private void unclassifyNotificationsForUserAndType(final int userId,
+            final @Types int bundleType) {
+        if (DBG) {
+            Slog.v(TAG,
+                    "unclassifyForUserAndType userId: " + userId + " bundleType: " + bundleType);
+        }
+        final String bundleChannelId = NotificationChannel.getChannelIdForBundleType(bundleType);
+        unclassifyNotificationsFiltered((r) ->
+                r.getUserId() == userId
+                && r.getChannel() != null
+                && Objects.equals(bundleChannelId, r.getChannel().getId()));
+    }
+
+    private void unclassifyNotificationsFiltered(Predicate<NotificationRecord> filter) {
+        if (!(notificationClassificationUi() && notificationRegroupOnClassification())) {
+            return;
+        }
+        synchronized (mNotificationLock) {
+            for (int i = 0; i < mEnqueuedNotifications.size(); i++) {
+                final NotificationRecord r = mEnqueuedNotifications.get(i);
+                if (filter.test(r)) {
+                    unclassifyNotificationLocked(r);
+                }
+            }
+
+            for (int i = 0; i < mNotificationList.size(); i++) {
+                final NotificationRecord r = mNotificationList.get(i);
+                if (filter.test(r)) {
+                    unclassifyNotificationLocked(r);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mNotificationLock")
+    private void unclassifyNotificationLocked(@NonNull final NotificationRecord r) {
+        if (DBG) {
+            Slog.v(TAG, "unclassifyNotification: " + r);
+        }
+
+        boolean hasOriginalSummary = false;
+        if (r.getSbn().isAppGroup() && r.getNotification().isGroupChild()) {
+            final String oldGroupKey = GroupHelper.getFullAggregateGroupKey(
+                    r.getSbn().getPackageName(), r.getOriginalGroupKey(), r.getUserId());
+            NotificationRecord groupSummary = mSummaryByGroupKey.get(oldGroupKey);
+            // We only care about app-provided valid groups
+            hasOriginalSummary = (groupSummary != null
+                    && !GroupHelper.isAggregatedGroup(groupSummary));
+        }
+
+        // Only NotificationRecord's mChannel is updated when bundled, the Notification
+        // mChannelId will always be the original channel.
+        String origChannelId = r.getNotification().getChannelId();
+        NotificationChannel originalChannel = mPreferencesHelper.getNotificationChannel(
+                r.getSbn().getPackageName(), r.getUid(), origChannelId, false);
+        String currChannelId = r.getChannel().getId();
+        boolean isBundled = NotificationChannel.SYSTEM_RESERVED_IDS.contains(currChannelId);
+        if (originalChannel != null && !origChannelId.equals(currChannelId) && isBundled) {
+            r.updateNotificationChannel(originalChannel);
+            mGroupHelper.onNotificationUnbundled(r, hasOriginalSummary);
+        }
+    }
+
+    @VisibleForTesting
+    void unclassifyNotification(final String key) {
+        if (!(notificationClassificationUi() && notificationRegroupOnClassification())) {
+            return;
+        }
+        synchronized (mNotificationLock) {
+            NotificationRecord r = mNotificationsByKey.get(key);
+            if (r == null) {
+                return;
+            }
+            unclassifyNotificationLocked(r);
+        }
+    }
+
+    @VisibleForTesting
+    void reclassifyNotification(String key) {
+        if (!(notificationClassificationUi() && notificationRegroupOnClassification())) {
+            return;
+        }
+        synchronized (mNotificationLock) {
+            NotificationRecord r = mNotificationsByKey.get(key);
+            if (r == null) {
+                return;
+            }
+            reclassifyNotificationLocked(r, true);
+        }
+    }
+
+    private void reclassifyNotificationsFiltered(Predicate<NotificationRecord> filter) {
+        if (!(notificationClassificationUi() && notificationRegroupOnClassification())) {
+            return;
+        }
+        synchronized (mNotificationLock) {
+            for (int i = 0; i < mEnqueuedNotifications.size(); i++) {
+                final NotificationRecord r = mEnqueuedNotifications.get(i);
+                if (filter.test(r)) {
+                    reclassifyNotificationLocked(r, false);
+                }
+            }
+
+            for (int i = 0; i < mNotificationList.size(); i++) {
+                final NotificationRecord r = mNotificationList.get(i);
+                if (filter.test(r)) {
+                    reclassifyNotificationLocked(r, true);
+                }
+            }
+        }
+    }
+
+    private void reclassifyNotificationsForUserAndType(final int userId,
+            final @Types int bundleType) {
+        if (DBG) {
+            Slog.v(TAG, "reclassifyNotificationsForUserAndType userId: " + userId + " bundleType: "
+                    + bundleType);
+        }
+        reclassifyNotificationsFiltered(
+                (r) -> r.getUserId() == userId && r.getBundleType() == bundleType);
+    }
+
+    private void reclassifyNotificationsForUid(final int userId, final String pkg) {
+        if (DBG) {
+            Slog.v(TAG, "reclassifyNotificationsForUid userId: " + userId + " pkg: " + pkg);
+        }
+        reclassifyNotificationsFiltered((r) ->
+                r.getUserId() == userId && Objects.equals(r.getSbn().getPackageName(), pkg));
+    }
+
+    private void reclassifyNotificationsForUser(final int userId) {
+        if (DBG) {
+            Slog.v(TAG, "reclassifyAllNotificationsForUser: " + userId);
+        }
+        reclassifyNotificationsFiltered((r) -> r.getUserId() == userId);
+    }
+
+    @GuardedBy("mNotificationLock")
+    private void reclassifyNotificationLocked(@NonNull final NotificationRecord r,
+            final boolean isPosted) {
+        if (DBG) {
+            Slog.v(TAG, "reclassifyNotification: " + r);
+        }
+
+        boolean isBundled = NotificationChannel.SYSTEM_RESERVED_IDS.contains(
+                r.getChannel().getId());
+        if (r.getBundleType() != Adjustment.TYPE_OTHER && !isBundled) {
+            final Bundle classifBundle = new Bundle();
+            classifBundle.putInt(KEY_TYPE, r.getBundleType());
+            Adjustment adj = new Adjustment(r.getSbn().getPackageName(), r.getKey(),
+                    classifBundle, "reclassify", r.getUserId());
+            applyAdjustmentLocked(r, adj, isPosted);
+            mRankingHandler.requestSort();
+        } else {
+            if (DBG) {
+                Slog.w(TAG, "Can't reclassify. No valid bundle type or already bundled: " + r);
+            }
+        }
+    }
+
     NotificationManagerPrivate mNotificationManagerPrivate = new NotificationManagerPrivate() {
         @Nullable
         @Override
@@ -2565,7 +2684,7 @@
 
     // TODO: All tests should use this init instead of the one-off setters above.
     @VisibleForTesting
-    void init(WorkerHandler handler, RankingHandler rankingHandler,
+    void init(WorkerHandler handler, RankingHandler rankingHandler, Handler broadcastsHandler,
             IPackageManager packageManager, PackageManager packageManagerClient,
             LightsManager lightsManager, NotificationListeners notificationListeners,
             NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
@@ -2585,6 +2704,9 @@
             ConnectivityManager connectivityManager,
             PostNotificationTrackerFactory postNotificationTrackerFactory) {
         mHandler = handler;
+        if (Flags.nmBinderPerfThrottleEffectsSuppressorBroadcast()) {
+            mBroadcastsHandler = broadcastsHandler;
+        }
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
                 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -2928,13 +3050,22 @@
 
         WorkerHandler handler = new WorkerHandler(Looper.myLooper());
 
+        Handler broadcastsHandler;
+        if (Flags.nmBinderPerfThrottleEffectsSuppressorBroadcast()) {
+            HandlerThread broadcastsThread = new HandlerThread("NMS Broadcasts");
+            broadcastsThread.start();
+            broadcastsHandler = new Handler(broadcastsThread.getLooper());
+        } else {
+            broadcastsHandler = null;
+        }
+
         mShowReviewPermissionsNotification = getContext().getResources().getBoolean(
                 R.bool.config_notificationReviewPermissions);
 
         mDefaultUnsupportedAdjustments = getContext().getResources().getStringArray(
                 R.array.config_notificationDefaultUnsupportedAdjustments);
 
-        init(handler, new RankingHandlerWorker(mRankingThread.getLooper()),
+        init(handler, new RankingHandlerWorker(mRankingThread.getLooper()), broadcastsHandler,
                 AppGlobals.getPackageManager(), getContext().getPackageManager(),
                 getLocalService(LightsManager.class),
                 new NotificationListeners(getContext(), mNotificationLock, mUserProfiles,
@@ -3169,6 +3300,25 @@
         sendRegisteredOnlyBroadcast(new Intent(action));
     }
 
+    /**
+     * Schedules a broadcast to be sent to runtime receivers and DND-policy-access packages. The
+     * broadcast will be sent after {@link #ZEN_BROADCAST_DELAY}, unless a new broadcast is
+     * scheduled in the interim, in which case the previous one is dropped and the waiting period
+     * is <em>restarted</em>.
+     *
+     * <p>Note that this uses <em>equality of the {@link Intent#getAction}</em> as the criteria for
+     * deduplicating pending broadcasts, ignoring the extras and anything else. This is intentional
+     * so that e.g. rapidly changing some value A -> B -> C will only produce a broadcast for C
+     * (instead of every time because the extras are different).
+     */
+    @FlaggedApi(Flags.FLAG_NM_BINDER_PERF_THROTTLE_EFFECTS_SUPPRESSOR_BROADCAST)
+    private void sendZenBroadcastWithDelay(Intent intent) {
+        String token = "zen_broadcast:" + intent.getAction();
+        mBroadcastsHandler.removeCallbacksAndEqualMessages(token);
+        mBroadcastsHandler.postDelayed(() -> sendRegisteredOnlyBroadcast(intent), token,
+                ZEN_BROADCAST_DELAY.toMillis());
+    }
+
     private void sendRegisteredOnlyBroadcast(Intent baseIntent) {
         int[] userIds = mUmInternal.getProfileIds(mAmi.getCurrentUserId(), true);
         if (Flags.nmBinderPerfReduceZenBroadcasts()) {
@@ -3362,14 +3512,25 @@
 
     @GuardedBy("mNotificationLock")
     private void updateEffectsSuppressorLocked() {
+        final long oldSuppressedEffects = mZenModeHelper.getSuppressedEffects();
         final long updatedSuppressedEffects = calculateSuppressedEffects();
-        if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
+        if (updatedSuppressedEffects == oldSuppressedEffects) return;
+
         final List<ComponentName> suppressors = getSuppressors();
         ZenLog.traceEffectsSuppressorChanged(
-                mEffectsSuppressors, suppressors, updatedSuppressedEffects);
-        mEffectsSuppressors = suppressors;
+                mEffectsSuppressors, suppressors, oldSuppressedEffects, updatedSuppressedEffects);
         mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
-        sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
+
+        if (Flags.nmBinderPerfThrottleEffectsSuppressorBroadcast()) {
+            if (!suppressors.equals(mEffectsSuppressors)) {
+                mEffectsSuppressors = suppressors;
+                sendZenBroadcastWithDelay(
+                        new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED));
+            }
+        } else {
+            mEffectsSuppressors = suppressors;
+            sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
+        }
     }
 
     private void exitIdle() {
@@ -3491,12 +3652,18 @@
     }
 
     private ArrayList<ComponentName> getSuppressors() {
-        ArrayList<ComponentName> names = new ArrayList<ComponentName>();
+        ArrayList<ComponentName> names = new ArrayList<>();
         for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
             ArraySet<ComponentName> serviceInfoList = mListenersDisablingEffects.valueAt(i);
 
             for (ComponentName info : serviceInfoList) {
-                names.add(info);
+                if (Flags.nmBinderPerfThrottleEffectsSuppressorBroadcast()) {
+                    if (!names.contains(info)) {
+                        names.add(info);
+                    }
+                } else {
+                    names.add(info);
+                }
             }
         }
 
@@ -4332,7 +4499,11 @@
         public void allowAssistantAdjustment(String adjustmentType) {
             checkCallerIsSystemOrSystemUiOrShell();
             mAssistants.allowAdjustmentType(adjustmentType);
-
+            if ((notificationClassificationUi() && notificationRegroupOnClassification())) {
+                if (KEY_TYPE.equals(adjustmentType)) {
+                    reclassifyNotificationsForUser(UserHandle.getUserId(Binder.getCallingUid()));
+                }
+            }
             handleSavePolicyFile();
         }
 
@@ -4341,7 +4512,11 @@
         public void disallowAssistantAdjustment(String adjustmentType) {
             checkCallerIsSystemOrSystemUiOrShell();
             mAssistants.disallowAdjustmentType(adjustmentType);
-
+            if ((notificationClassificationUi() && notificationRegroupOnClassification())) {
+                if (KEY_TYPE.equals(adjustmentType)) {
+                    unclassifyNotificationsForUser(UserHandle.getUserId(Binder.getCallingUid()));
+                }
+            }
             handleSavePolicyFile();
         }
 
@@ -4378,7 +4553,7 @@
         @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
         public @NonNull int[] getAllowedAdjustmentKeyTypes() {
             checkCallerIsSystemOrSystemUiOrShell();
-            return mAssistants.getAllowedAdjustmentKeyTypes();
+            return mAssistants.getAllowedClassificationTypes();
         }
 
         @Override
@@ -4386,23 +4561,39 @@
         public void setAssistantAdjustmentKeyTypeState(int type, boolean enabled) {
             checkCallerIsSystemOrSystemUiOrShell();
             mAssistants.setAssistantAdjustmentKeyTypeState(type, enabled);
-
+            if ((notificationClassificationUi() && notificationRegroupOnClassification())) {
+                if (enabled) {
+                    reclassifyNotificationsForUserAndType(
+                            UserHandle.getUserId(Binder.getCallingUid()), type);
+                } else {
+                    unclassifyNotificationsForUserAndType(
+                            UserHandle.getUserId(Binder.getCallingUid()), type);
+                }
+            }
             handleSavePolicyFile();
         }
 
         @Override
-        @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-        public @NonNull String[] getTypeAdjustmentDeniedPackages() {
+        public boolean isAdjustmentSupportedForPackage(String key, String pkg) {
             checkCallerIsSystemOrSystemUiOrShell();
-            return mAssistants.getTypeAdjustmentDeniedPackages();
+            return mAssistants.isAdjustmentAllowedForPackage(key, pkg);
         }
 
         @Override
-        @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-        public void setTypeAdjustmentForPackageState(String pkg, boolean enabled) {
+        public void setAdjustmentSupportedForPackage(@Adjustment.Keys String key, String pkg,
+                boolean enabled) {
             checkCallerIsSystemOrSystemUiOrShell();
-            mAssistants.setTypeAdjustmentForPackageState(pkg, enabled);
-
+            mAssistants.setAdjustmentSupportedForPackage(key, pkg, enabled);
+            if (notificationClassificationUi() && notificationRegroupOnClassification()
+                    && key.equals(KEY_TYPE)) {
+                if (enabled) {
+                    reclassifyNotificationsForUid(UserHandle.getUserId(Binder.getCallingUid()),
+                            pkg);
+                } else {
+                    unclassifyNotificationsForUid(UserHandle.getUserId(Binder.getCallingUid()),
+                            pkg);
+                }
+            }
             handleSavePolicyFile();
         }
 
@@ -5018,14 +5209,8 @@
         }
 
         @Override
-        public ParceledListSlice<NotificationChannel> getNotificationChannels(
-                String callingPkg, String targetPkg, int userId) {
-            return getOrCreateNotificationChannels(callingPkg, targetPkg, userId, false);
-        }
-
-        @Override
-        public ParceledListSlice<NotificationChannel> getOrCreateNotificationChannels(
-                String callingPkg, String targetPkg, int userId, boolean createPrefsIfNeeded) {
+        public ParceledListSlice<NotificationChannel> getNotificationChannels(String callingPkg,
+                String targetPkg, int userId) {
             if (canNotifyAsPackage(callingPkg, targetPkg, userId)
                 || isCallingUidSystem()) {
                 int targetUid = -1;
@@ -5035,8 +5220,7 @@
                     /* ignore */
                 }
                 return mPreferencesHelper.getNotificationChannels(
-                        targetPkg, targetUid, false /* includeDeleted */, true,
-                        createPrefsIfNeeded);
+                        targetPkg, targetUid, false /* includeDeleted */, true);
             }
             throw new SecurityException("Pkg " + callingPkg
                     + " cannot read channels for " + targetPkg + " in " + userId);
@@ -7207,17 +7391,28 @@
         if (adjustment.getSignals() != null) {
             final Bundle adjustments = adjustment.getSignals();
             Bundle.setDefusable(adjustments, true);
+            // Save classification even if the adjustment is disabled, in case user enables it later
+            if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) {
+                r.setBundleType(adjustments.getInt(KEY_TYPE));
+            }
             List<String> toRemove = new ArrayList<>();
             for (String potentialKey : adjustments.keySet()) {
                 if (!mAssistants.isAdjustmentAllowed(potentialKey)) {
                     toRemove.add(potentialKey);
                 }
-                if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) {
+                if (notificationClassification() && potentialKey.equals(KEY_TYPE)) {
                     mAssistants.setNasUnsupportedDefaults(r.getSbn().getNormalizedUserId());
                     if (!mAssistants.isAdjustmentKeyTypeAllowed(adjustments.getInt(KEY_TYPE))) {
                         toRemove.add(potentialKey);
                     } else if (notificationClassificationUi()
-                            && !mAssistants.isTypeAdjustmentAllowedForPackage(
+                            && !mAssistants.isAdjustmentAllowedForPackage(KEY_TYPE,
+                            r.getSbn().getPackageName())) {
+                        toRemove.add(potentialKey);
+                    }
+                }
+                if ((nmSummarization() || nmSummarizationUi())
+                        && potentialKey.equals(KEY_SUMMARIZATION)) {
+                    if (!mAssistants.isAdjustmentAllowedForPackage(KEY_SUMMARIZATION,
                             r.getSbn().getPackageName())) {
                         toRemove.add(potentialKey);
                     }
@@ -7236,9 +7431,7 @@
                     int classification = adjustments.getInt(KEY_TYPE);
                     // swap app provided type with the real thing
                     adjustments.putParcelable(KEY_TYPE, newChannel);
-
                     logClassificationChannelAdjustmentReceived(r, isPosted, classification);
-                    r.setBundleType(classification);
                 }
             }
             r.addAdjustment(adjustment);
@@ -8467,6 +8660,9 @@
                 (userId == USER_ALL) ? USER_SYSTEM : userId);
         Notification.addFieldsFromContext(ai, notification);
 
+        // can't be set by an app
+        notification.extras.remove(Notification.EXTRA_SUMMARIZED_CONTENT);
+
         if (notification.isForegroundService() && fgsPolicy == NOT_FOREGROUND_SERVICE) {
             notification.flags &= ~FLAG_FOREGROUND_SERVICE;
         }
@@ -8717,7 +8913,7 @@
         if (contentView == null) {
             return false;
         }
-        final int contentViewSize = contentView.estimateMemoryUsage();
+        final long contentViewSize = contentView.estimateMemoryUsage();
         if (contentViewSize > mWarnRemoteViewsSizeBytes
                 && contentViewSize < mStripRemoteViewsSizeBytes) {
             Slog.w(TAG, "RemoteViews too large on pkg: " + pkg + " tag: " + tag + " id: " + id
@@ -10395,7 +10591,8 @@
                         r.getRankingScore(),
                         r.isConversation(),
                         r.getProposedImportance(),
-                        r.hasSensitiveContent());
+                        r.hasSensitiveContent(),
+                        r.getSummarization());
                 extractorDataBefore.put(r.getKey(), extractorData);
                 mRankingHelper.extractSignals(r);
             }
@@ -11715,7 +11912,8 @@
                             : (record.getRankingScore() > 0 ?  RANKING_PROMOTED : RANKING_DEMOTED),
                     record.getNotification().isBubbleNotification(),
                     record.getProposedImportance(),
-                    hasSensitiveContent
+                    hasSensitiveContent,
+                    record.getSummarization()
             );
             rankings.add(ranking);
         }
@@ -11867,15 +12065,17 @@
         static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
 
         private static final String ATT_TYPES = "types";
-        private static final String ATT_DENIED = "user_denied_adjustments";
-        private static final String ATT_ENABLED_TYPES = "enabled_key_types";
+        private static final String TAG_DENIED = "user_denied_adjustments";
+        private static final String TAG_DENIED_KEY = "adjustment";
+        private static final String ATT_DENIED_KEY = "key";
+        private static final String ATT_DENIED_KEY_APPS = "denied_apps";
+        private static final String TAG_ENABLED_TYPES = "enabled_key_types";
         private static final String ATT_NAS_UNSUPPORTED = "nas_unsupported_adjustments";
-        private static final String ATT_TYPES_DENIED_APPS = "types_denied_apps";
 
         private final Object mLock = new Object();
 
         @GuardedBy("mLock")
-        private Set<Integer> mAllowedAdjustmentKeyTypes = new ArraySet<>();
+        private Set<Integer> mAllowedClassificationTypes = new ArraySet<>();
 
         @GuardedBy("mLock")
         private Set<String> mAllowedAdjustments = new ArraySet<>();
@@ -11886,8 +12086,10 @@
         @GuardedBy("mLock")
         private Map<Integer, HashSet<String>> mNasUnsupported = new ArrayMap<>();
 
+        // key: Adjustment key. value - list of pkgs that we shouldn't apply adjustments with that
+        // key to
         @GuardedBy("mLock")
-        private Set<String> mClassificationTypeDeniedPackages = new ArraySet<>();
+        private Map<String, Set<String>> mAdjustmentKeyDeniedPackages = new ArrayMap<>();
 
         protected ComponentName mDefaultFromConfig = null;
 
@@ -11964,7 +12166,7 @@
                     mAllowedAdjustments.add(DEFAULT_ALLOWED_ADJUSTMENTS[i]);
                 }
             } else {
-                mAllowedAdjustmentKeyTypes.addAll(List.of(DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES));
+                mAllowedClassificationTypes.addAll(List.of(DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES));
             }
         }
 
@@ -12056,17 +12258,17 @@
         protected @NonNull boolean isAdjustmentKeyTypeAllowed(@Adjustment.Types int type) {
             synchronized (mLock) {
                 if (notificationClassification()) {
-                    return mAllowedAdjustmentKeyTypes.contains(type);
+                    return mAllowedClassificationTypes.contains(type);
                 }
             }
             return false;
         }
 
         @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
-        protected @NonNull int[] getAllowedAdjustmentKeyTypes() {
+        protected @NonNull int[] getAllowedClassificationTypes() {
             synchronized (mLock) {
                 if (notificationClassification()) {
-                    return mAllowedAdjustmentKeyTypes.stream()
+                    return mAllowedClassificationTypes.stream()
                             .mapToInt(Integer::intValue).toArray();
                 }
             }
@@ -12081,47 +12283,35 @@
             }
             synchronized (mLock) {
                 if (enabled) {
-                    mAllowedAdjustmentKeyTypes.add(type);
+                    mAllowedClassificationTypes.add(type);
                 } else {
-                    mAllowedAdjustmentKeyTypes.remove(type);
+                    mAllowedClassificationTypes.remove(type);
                 }
             }
         }
 
-        @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-        protected @NonNull boolean isTypeAdjustmentAllowedForPackage(String pkg) {
+        protected @NonNull boolean isAdjustmentAllowedForPackage(@Adjustment.Keys String key,
+                String pkg) {
             synchronized (mLock) {
-                if (notificationClassificationUi()) {
-                    return !mClassificationTypeDeniedPackages.contains(pkg);
+                if (notificationClassificationUi() || nmSummarization() | nmSummarizationUi()) {
+                    return !mAdjustmentKeyDeniedPackages.getOrDefault(
+                            key, new ArraySet<>()).contains(pkg);
                 }
             }
             return true;
         }
 
-        @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-        protected @NonNull String[] getTypeAdjustmentDeniedPackages() {
-            synchronized (mLock) {
-                if (notificationClassificationUi()) {
-                    return mClassificationTypeDeniedPackages.toArray(new String[0]);
-                }
-            }
-            return new String[]{};
-        }
-
-        /**
-         * Set whether a particular package can have its notification channels adjusted to have a
-         * different type by NotificationAssistants.
-         */
-        @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-        public void setTypeAdjustmentForPackageState(String pkg, boolean enabled) {
-            if (!notificationClassificationUi()) {
+        public void setAdjustmentSupportedForPackage(@Adjustment.Keys String key, String pkg,
+                boolean enabled) {
+            if (!(notificationClassificationUi() || nmSummarization() | nmSummarizationUi())) {
                 return;
             }
             synchronized (mLock) {
+                mAdjustmentKeyDeniedPackages.putIfAbsent(key, new ArraySet<>());
                 if (enabled) {
-                    mClassificationTypeDeniedPackages.remove(pkg);
+                    mAdjustmentKeyDeniedPackages.get(key).remove(pkg);
                 } else {
-                    mClassificationTypeDeniedPackages.add(pkg);
+                    mAdjustmentKeyDeniedPackages.get(key).add(pkg);
                 }
             }
         }
@@ -12585,32 +12775,35 @@
 
         @Override
         protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
-            if (!notificationClassification()) {
+            if (!(notificationClassificationUi() || nmSummarization() || nmSummarizationUi())) {
                 return;
             }
             synchronized (mLock) {
-                out.startTag(null, ATT_DENIED);
+                out.startTag(null, TAG_DENIED);
                 out.attribute(null, ATT_TYPES, TextUtils.join(",", mDeniedAdjustments));
-                out.endTag(null, ATT_DENIED);
-                out.startTag(null, ATT_ENABLED_TYPES);
-                out.attribute(null, ATT_TYPES,
-                        TextUtils.join(",", mAllowedAdjustmentKeyTypes));
-                out.endTag(null, ATT_ENABLED_TYPES);
-                if (notificationClassificationUi()) {
-                    out.startTag(null, ATT_TYPES_DENIED_APPS);
-                    out.attribute(null, ATT_TYPES,
-                            TextUtils.join(",", mClassificationTypeDeniedPackages));
-                    out.endTag(null, ATT_TYPES_DENIED_APPS);
+                for (String key : mAdjustmentKeyDeniedPackages.keySet()) {
+                    Set<String> pkgs = mAdjustmentKeyDeniedPackages.get(key);
+                    if (pkgs != null && !pkgs.isEmpty()) {
+                        out.startTag(null, TAG_DENIED_KEY);
+                        out.attribute(null, ATT_DENIED_KEY, key);
+                        out.attribute(null, ATT_DENIED_KEY_APPS, TextUtils.join(",", pkgs));
+                        out.endTag(null, TAG_DENIED_KEY);
+                    }
                 }
+                out.endTag(null, TAG_DENIED);
+                out.startTag(null, TAG_ENABLED_TYPES);
+                out.attribute(null, ATT_TYPES,
+                        TextUtils.join(",", mAllowedClassificationTypes));
+                out.endTag(null, TAG_ENABLED_TYPES);
             }
         }
 
         @Override
         protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
-            if (!notificationClassification()) {
+            if (!(notificationClassificationUi() || nmSummarization() || nmSummarizationUi())) {
                 return;
             }
-            if (ATT_DENIED.equals(tag)) {
+            if (TAG_DENIED.equals(tag)) {
                 final String keys = XmlUtils.readStringAttribute(parser, ATT_TYPES);
                 synchronized (mLock) {
                     mDeniedAdjustments.clear();
@@ -12618,28 +12811,27 @@
                         mDeniedAdjustments.addAll(Arrays.asList(keys.split(",")));
                     }
                 }
-            } else if (ATT_ENABLED_TYPES.equals(tag)) {
+            } else if (TAG_ENABLED_TYPES.equals(tag)) {
                 final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
                 synchronized (mLock) {
-                    mAllowedAdjustmentKeyTypes.clear();
+                    mAllowedClassificationTypes.clear();
                     if (!TextUtils.isEmpty(types)) {
                         List<String> typeList = Arrays.asList(types.split(","));
                         for (String type : typeList) {
                             try {
-                                mAllowedAdjustmentKeyTypes.add(Integer.parseInt(type));
+                                mAllowedClassificationTypes.add(Integer.parseInt(type));
                             } catch (NumberFormatException e) {
                                 Slog.wtf(TAG, "Bad type specified", e);
                             }
                         }
                     }
                 }
-            } else if (notificationClassificationUi() && ATT_TYPES_DENIED_APPS.equals(tag)) {
-                final String apps = XmlUtils.readStringAttribute(parser, ATT_TYPES);
-                synchronized (mLock) {
-                    mClassificationTypeDeniedPackages.clear();
-                    if (!TextUtils.isEmpty(apps)) {
-                        mClassificationTypeDeniedPackages.addAll(Arrays.asList(apps.split(",")));
-                    }
+            } else if (TAG_DENIED_KEY.equals(tag)) {
+                final String key = XmlUtils.readStringAttribute(parser, ATT_DENIED_KEY);
+                final String pkgs = XmlUtils.readStringAttribute(parser, ATT_DENIED_KEY_APPS);
+                if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(pkgs)) {
+                    List<String> pkgList = Arrays.asList(pkgs.split(","));
+                    mAdjustmentKeyDeniedPackages.put(key, new ArraySet<>(pkgList));
                 }
             }
         }
@@ -12668,7 +12860,7 @@
                 bundlesAllowed = !unsupportedAdjustments.contains(Adjustment.KEY_TYPE);
             }
 
-            int[] allowedBundleTypes = getAllowedAdjustmentKeyTypes();
+            int[] allowedBundleTypes = getAllowedClassificationTypes();
 
             events.add(FrameworkStatsLog.buildStatsEvent(
                     NOTIFICATION_BUNDLE_PREFERENCES,
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 81af0d8..1def7ec 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,6 +25,7 @@
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
 
@@ -225,6 +226,8 @@
     // type of the bundle if the notification was classified
     private @Adjustment.Types int mBundleType = Adjustment.TYPE_OTHER;
 
+    private String mSummarization = null;
+
     public NotificationRecord(Context context, StatusBarNotification sbn,
             NotificationChannel channel) {
         this.sbn = sbn;
@@ -470,10 +473,6 @@
             }
         }
 
-        if (android.service.notification.Flags.notificationClassification()) {
-            mBundleType = previous.mBundleType;
-        }
-
         // Don't copy importance information or mGlobalSortKey, recompute them.
     }
 
@@ -589,6 +588,7 @@
         pw.println(prefix + "shortcut=" + notification.getShortcutId()
                 + " found valid? " + (mShortcutInfo != null));
         pw.println(prefix + "mUserVisOverride=" + getPackageVisibilityOverride());
+        pw.println(prefix + "hasSummarization=" + (mSummarization != null));
     }
 
     private void dumpNotification(PrintWriter pw, String prefix, Notification notification,
@@ -811,6 +811,12 @@
                             Adjustment.KEY_TYPE,
                             mChannel.getId());
                 }
+                if ((android.app.Flags.nmSummarizationUi() || android.app.Flags.nmSummarization())
+                        && signals.containsKey(KEY_SUMMARIZATION)) {
+                    mSummarization = signals.getString(KEY_SUMMARIZATION);
+                    EventLogTags.writeNotificationAdjusted(getKey(),
+                            KEY_SUMMARIZATION, Boolean.toString(mSummarization != null));
+                }
                 if (!signals.isEmpty() && adjustment.getIssuer() != null) {
                     mAdjustmentIssuer = adjustment.getIssuer();
                 }
@@ -983,6 +989,13 @@
         return null;
     }
 
+    public String getSummarization() {
+        if ((android.app.Flags.nmSummarizationUi() || android.app.Flags.nmSummarization())) {
+            return mSummarization;
+        }
+        return null;
+    }
+
     public boolean setIntercepted(boolean intercept) {
         mIntercept = intercept;
         mInterceptSet = true;
diff --git a/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java b/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java
index 3f4f7d3..9315ddc 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java
@@ -47,6 +47,7 @@
     private final boolean mIsConversation;
     private final int mProposedImportance;
     private final boolean mSensitiveContent;
+    private final String mSummarization;
 
     NotificationRecordExtractorData(int position, int visibility, boolean showBadge,
             boolean allowBubble, boolean isBubble, NotificationChannel channel, String groupKey,
@@ -54,7 +55,8 @@
             Integer userSentiment, Integer suppressVisually,
             ArrayList<Notification.Action> systemSmartActions,
             ArrayList<CharSequence> smartReplies, int importance, float rankingScore,
-            boolean isConversation, int proposedImportance, boolean sensitiveContent) {
+            boolean isConversation, int proposedImportance, boolean sensitiveContent,
+            String summarization) {
         mPosition = position;
         mVisibility = visibility;
         mShowBadge = showBadge;
@@ -73,6 +75,7 @@
         mIsConversation = isConversation;
         mProposedImportance = proposedImportance;
         mSensitiveContent = sensitiveContent;
+        mSummarization = summarization;
     }
 
     // Returns whether the provided NotificationRecord differs from the cached data in any way.
@@ -93,7 +96,8 @@
                 || !Objects.equals(mSmartReplies, r.getSmartReplies())
                 || mImportance != r.getImportance()
                 || mProposedImportance != r.getProposedImportance()
-                || mSensitiveContent != r.hasSensitiveContent();
+                || mSensitiveContent != r.hasSensitiveContent()
+                || !Objects.equals(mSummarization, r.getSummarization());
     }
 
     // Returns whether the NotificationRecord has a change from this data for which we should
@@ -117,6 +121,7 @@
                 || !r.rankingScoreMatches(mRankingScore)
                 || mIsConversation != r.isConversation()
                 || mProposedImportance != r.getProposedImportance()
-                || mSensitiveContent != r.hasSensitiveContent();
+                || mSensitiveContent != r.hasSensitiveContent()
+                || !Objects.equals(mSummarization, r.getSummarization());
     }
 }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3b34dcd..7d45cd9 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -16,8 +16,8 @@
 
 package com.android.server.notification;
 
-import static android.app.Flags.notificationClassificationUi;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+import static android.app.Flags.notificationClassificationUi;
 import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
 import static android.app.NotificationChannel.NEWS_ID;
 import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
@@ -34,10 +34,6 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 import static android.os.UserHandle.USER_SYSTEM;
-import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
-import static android.service.notification.Adjustment.TYPE_NEWS;
-import static android.service.notification.Adjustment.TYPE_PROMOTION;
-import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
 import static android.service.notification.Flags.notificationClassification;
 
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
@@ -89,9 +85,10 @@
 import android.util.StatsEvent;
 import android.util.proto.ProtoOutputStream;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
 import com.android.internal.logging.MetricsLogger;
@@ -1427,20 +1424,6 @@
         }
     }
 
-    private @Nullable String getChannelIdForBundleType(@Adjustment.Types int type) {
-        switch (type) {
-            case TYPE_CONTENT_RECOMMENDATION:
-                return RECS_ID;
-            case TYPE_NEWS:
-                return NEWS_ID;
-            case TYPE_PROMOTION:
-                return PROMOTIONS_ID;
-            case TYPE_SOCIAL_MEDIA:
-                return SOCIAL_MEDIA_ID;
-        }
-        return null;
-    }
-
     @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
     public NotificationChannel getReservedChannel(String pkg, int uid,
             @Adjustment.Types int type) {
@@ -1448,7 +1431,7 @@
             return null;
         }
         Objects.requireNonNull(pkg);
-        String channelId = getChannelIdForBundleType(type);
+        String channelId = NotificationChannel.getChannelIdForBundleType(type);
         if (channelId == null) {
             return null;
         }
@@ -1468,7 +1451,7 @@
         if (r == null) {
             return null;
         }
-        String channelId = getChannelIdForBundleType(type);
+        String channelId = NotificationChannel.getChannelIdForBundleType(type);
         if (channelId == null) {
             return null;
         }
@@ -1962,21 +1945,11 @@
     @Override
     public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
             boolean includeDeleted, boolean includeBundles) {
-        return getNotificationChannels(pkg, uid, includeDeleted, includeBundles, false);
-    }
-
-    protected ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
-            boolean includeDeleted, boolean includeBundles, boolean createPrefsIfNeeded) {
-        if (createPrefsIfNeeded && !android.app.Flags.nmBinderPerfCacheChannels()) {
-            Slog.wtf(TAG,
-                    "getNotificationChannels called with createPrefsIfNeeded=true and flag off");
-            createPrefsIfNeeded = false;
-        }
         Objects.requireNonNull(pkg);
         List<NotificationChannel> channels = new ArrayList<>();
         synchronized (mLock) {
             PackagePreferences r;
-            if (createPrefsIfNeeded) {
+            if (android.app.Flags.nmBinderPerfCacheChannels()) {
                 r = getOrCreatePackagePreferencesLocked(pkg, uid);
             } else {
                 r = getPackagePreferencesLocked(pkg, uid);
@@ -1997,6 +1970,18 @@
         }
     }
 
+    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+    // Gets the entire list of notification channels for this package, with no filtering and
+    // without creating package preferences. For testing only, specifically to confirm the
+    // notification channels of a removed/deleted package.
+    protected List<NotificationChannel> getRemovedPkgNotificationChannels(String pkg, int uid) {
+        PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+        if (r == null || r.channels == null) {
+            return new ArrayList<>();
+        }
+        return new ArrayList<>(r.channels.values());
+    }
+
     /**
      * Gets all notification channels associated with the given pkg and uid that can bypass dnd
      */
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 7e853d9..49f93b8 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -140,8 +140,9 @@
     }
 
     public static void traceEffectsSuppressorChanged(List<ComponentName> oldSuppressors,
-            List<ComponentName> newSuppressors, long suppressedEffects) {
-        append(TYPE_SUPPRESSOR_CHANGED, "suppressed effects:" + suppressedEffects + ","
+            List<ComponentName> newSuppressors, long oldSuppressedEffects, long suppressedEffects) {
+        append(TYPE_SUPPRESSOR_CHANGED, "suppressed effects:"
+                + oldSuppressedEffects + "->" + suppressedEffects + ","
                 + componentListToString(oldSuppressors) + "->"
                 + componentListToString(newSuppressors));
     }
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 822ff48..048f2b6 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -182,6 +182,16 @@
 }
 
 flag {
+  name: "nm_binder_perf_throttle_effects_suppressor_broadcast"
+  namespace: "systemui"
+  description: "Delay sending the ACTION_EFFECTS_SUPPRESSOR_CHANGED broadcast if it changes too often"
+  bug: "371776935"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "fix_calling_uid_from_cps"
   namespace: "systemui"
   description: "Correctly checks zen rule ownership when a CPS notifies with a Condition"
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index b8b49f3e..f9758fc 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -26,13 +26,13 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
@@ -49,7 +49,6 @@
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
-import java.util.stream.Stream;
 
 /**
  * Data structure representing the current state of all overlay packages in the
@@ -358,26 +357,29 @@
     }
 
     void dump(@NonNull final PrintWriter p, @NonNull DumpState dumpState) {
-        // select items to display
-        Stream<SettingsItem> items = mItems.stream();
-        if (dumpState.getUserId() != UserHandle.USER_ALL) {
-            items = items.filter(item -> item.mUserId == dumpState.getUserId());
-        }
-        if (dumpState.getPackageName() != null) {
-            items = items.filter(item -> item.mOverlay.getPackageName()
-                    .equals(dumpState.getPackageName()));
-        }
-        if (dumpState.getOverlayName() != null) {
-            items = items.filter(item -> item.mOverlay.getOverlayName()
-                    .equals(dumpState.getOverlayName()));
-        }
+        final int userId = dumpState.getUserId();
+        final String packageName = dumpState.getPackageName();
+        final String overlayName = dumpState.getOverlayName();
+        final String field = dumpState.getField();
+        final var pw = new IndentingPrintWriter(p, "  ");
 
-        // display items
-        final IndentingPrintWriter pw = new IndentingPrintWriter(p, "  ");
-        if (dumpState.getField() != null) {
-            items.forEach(item -> dumpSettingsItemField(pw, item, dumpState.getField()));
-        } else {
-            items.forEach(item -> dumpSettingsItem(pw, item));
+        for (int i = 0; i < mItems.size(); i++) {
+            final var item = mItems.get(i);
+            if (userId != UserHandle.USER_ALL && userId != item.mUserId) {
+                continue;
+            }
+            if (packageName != null && !packageName.equals(item.mOverlay.getPackageName())) {
+                continue;
+            }
+            if (overlayName != null && !overlayName.equals(item.mOverlay.getOverlayName())) {
+                continue;
+            }
+
+            if (field != null) {
+                dumpSettingsItemField(pw, item, field);
+            } else {
+                dumpSettingsItem(pw, item);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pinner/PinnerService.java b/services/core/java/com/android/server/pinner/PinnerService.java
index 2c75926..5ed2688 100644
--- a/services/core/java/com/android/server/pinner/PinnerService.java
+++ b/services/core/java/com/android/server/pinner/PinnerService.java
@@ -1305,11 +1305,13 @@
                     mConfiguredMaxPinnedMemoryPercentage);
             pw.format("   Maximum Pinner quota: %d bytes (%.2f MB)\n", mConfiguredMaxPinnedMemory,
                     mConfiguredMaxPinnedMemory / bytesPerMB);
-            pw.format("   Max Home App Pin Bytes (without deps): %d\n", mConfiguredHomePinBytes);
-            pw.format("   Max Assistant App Pin Bytes (without deps): %d\n",
-                    mConfiguredAssistantPinBytes);
+            pw.format("   Max Home App Pin Bytes (without deps): %d (%.2f MB)\n",
+                    mConfiguredHomePinBytes, mConfiguredHomePinBytes / bytesPerMB);
+            pw.format("   Max Assistant App Pin Bytes (without deps): %d (%.2f MB)\n",
+                    mConfiguredAssistantPinBytes, mConfiguredAssistantPinBytes / bytesPerMB);
             pw.format(
-                    "   Max Camera App Pin Bytes (without deps): %d\n", mConfiguredCameraPinBytes);
+                    "   Max Camera App Pin Bytes (without deps): %d (%.2f MB)\n",
+                    mConfiguredCameraPinBytes, mConfiguredCameraPinBytes / bytesPerMB);
             pw.format("\nPinned Files:\n");
             synchronized (PinnerService.this) {
                 long totalSize = 0;
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 0b58c75..f011d28 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -96,6 +96,9 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
 
@@ -105,6 +108,11 @@
 public final class DexOptHelper {
     private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
 
+    @NonNull
+    private static final ThreadPoolExecutor sDexoptExecutor =
+            new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */,
+                    60 /* keepAliveTime */, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+
     private static boolean sArtManagerLocalIsInitialized = false;
 
     private final PackageManagerService mPm;
@@ -113,6 +121,11 @@
     // used, to make it available to the onDexoptDone callback.
     private volatile long mBootDexoptStartTime;
 
+    static {
+        // Recycle the thread if it's not used for `keepAliveTime`.
+        sDexoptExecutor.allowsCoreThreadTimeOut();
+    }
+
     DexOptHelper(PackageManagerService pm) {
         mPm = pm;
     }
@@ -746,43 +759,11 @@
      */
     static void performDexoptIfNeeded(InstallRequest installRequest, DexManager dexManager,
             Context context, PackageManagerTracedLock.RawLock installLock) {
-
         // Construct the DexoptOptions early to see if we should skip running dexopt.
-        //
-        // Do not run PackageDexOptimizer through the local performDexOpt
-        // method because `pkg` may not be in `mPackages` yet.
-        //
-        // Also, don't fail application installs if the dexopt step fails.
         DexoptOptions dexoptOptions = getDexoptOptionsByInstallRequest(installRequest, dexManager);
-        // Check whether we need to dexopt the app.
-        //
-        // NOTE: it is IMPORTANT to call dexopt:
-        //   - after doRename which will sync the package data from AndroidPackage and
-        //     its corresponding ApplicationInfo.
-        //   - after installNewPackageLIF or replacePackageLIF which will update result with the
-        //     uid of the application (pkg.applicationInfo.uid).
-        //     This update happens in place!
-        //
-        // We only need to dexopt if the package meets ALL of the following conditions:
-        //   1) it is not an instant app or if it is then dexopt is enabled via gservices.
-        //   2) it is not debuggable.
-        //   3) it is not on Incremental File System.
-        //
-        // Note that we do not dexopt instant apps by default. dexopt can take some time to
-        // complete, so we skip this step during installation. Instead, we'll take extra time
-        // the first time the instant app starts. It's preferred to do it this way to provide
-        // continuous progress to the useur instead of mysteriously blocking somewhere in the
-        // middle of running an instant app. The default behaviour can be overridden
-        // via gservices.
-        //
-        // Furthermore, dexopt may be skipped, depending on the install scenario and current
-        // state of the device.
-        //
-        // TODO(b/174695087): instantApp and onIncremental should be removed and their install
-        //       path moved to SCENARIO_FAST.
+        boolean performDexopt =
+                DexOptHelper.shouldPerformDexopt(installRequest, dexoptOptions, context);
 
-        final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest,
-                dexoptOptions, context);
         if (performDexopt) {
             // 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.
@@ -791,6 +772,7 @@
             }
             try {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+                // Don't fail application installs if the dexopt step fails.
                 DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
                         installRequest, dexoptOptions);
                 installRequest.onDexoptFinished(dexOptResult);
@@ -804,6 +786,41 @@
     }
 
     /**
+     * Same as above, but runs asynchronously.
+     */
+    static CompletableFuture<Void> performDexoptIfNeededAsync(InstallRequest installRequest,
+            DexManager dexManager, Context context) {
+        // Construct the DexoptOptions early to see if we should skip running dexopt.
+        DexoptOptions dexoptOptions = getDexoptOptionsByInstallRequest(installRequest, dexManager);
+        boolean performDexopt =
+                DexOptHelper.shouldPerformDexopt(installRequest, dexoptOptions, context);
+
+        if (performDexopt) {
+            return CompletableFuture
+                    .runAsync(() -> {
+                        try {
+                            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+                            // Don't fail application installs if the dexopt step fails.
+                            // TODO(jiakaiz): Make this async in ART Service.
+                            DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
+                                    installRequest, dexoptOptions);
+                            installRequest.onDexoptFinished(dexOptResult);
+                        } finally {
+                            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                        }
+                    }, sDexoptExecutor)
+                    .exceptionally((t) -> {
+                        // This should never happen. A normal dexopt failure should result in a
+                        // DexoptResult.DEXOPT_FAILED, not an exception.
+                        Slog.wtf(TAG, "Dexopt encountered a fatal error", t);
+                        return null;
+                    });
+        } else {
+            return CompletableFuture.completedFuture(null);
+        }
+    }
+
+    /**
      * Use ArtService to perform dexopt by the given InstallRequest.
      */
     static DexoptResult dexoptPackageUsingArtService(InstallRequest installRequest,
@@ -840,6 +857,20 @@
      */
     static boolean shouldPerformDexopt(InstallRequest installRequest, DexoptOptions dexoptOptions,
             Context context) {
+        // We only need to dexopt if the package meets ALL of the following conditions:
+        //   1) it is not an instant app or if it is then dexopt is enabled via gservices.
+        //   2) it is not debuggable.
+        //   3) it is not on Incremental File System.
+        //
+        // Note that we do not dexopt instant apps by default. dexopt can take some time to
+        // complete, so we skip this step during installation. Instead, we'll take extra time
+        // the first time the instant app starts. It's preferred to do it this way to provide
+        // continuous progress to the user instead of mysteriously blocking somewhere in the
+        // middle of running an instant app. The default behaviour can be overridden
+        // via gservices.
+        //
+        // Furthermore, dexopt may be skipped, depending on the install scenario and current
+        // state of the device.
         final boolean isApex = ((installRequest.getScanFlags() & SCAN_AS_APEX) != 0);
         final boolean instantApp = ((installRequest.getScanFlags() & SCAN_AS_INSTANT_APP) != 0);
         final PackageSetting ps = installRequest.getScannedPackageSetting();
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8eb5b6f..0c27823 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1158,6 +1158,7 @@
                 return;
             }
             request.setKeepArtProfile(true);
+            // TODO(b/388159696): Use performDexoptIfNeededAsync.
             DexOptHelper.performDexoptIfNeeded(request, mDexManager, mContext, null);
         }
     }
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 14b0fc8..c62aaeb 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -584,6 +584,12 @@
      * Returns the user id of the main user, or {@link android.os.UserHandle#USER_NULL} if there is
      * no main user.
      *
+     * <p>NB: Features should ideally not limit functionality to the main user. Ideally, they
+     * should either work for all users or for all admin users. If a feature should only work for
+     * select users, its determination of which user should be done intelligently or be
+     * customizable. Not all devices support a main user, and the idea of singling out one user as
+     * special is contrary to overall multiuser goals.
+     *
      * @see UserManager#isMainUser()
      */
     public abstract @UserIdInt int getMainUserId();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a2c53e5..8cbccf5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3590,8 +3590,6 @@
     }
 
     /**
-     * @hide
-     *
      * Returns who set a user restriction on a user.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @param restrictionKey the string key representing the restriction
@@ -6275,9 +6273,6 @@
         }
     }
 
-    /**
-     * @hide
-     */
     @Override
     public @NonNull UserInfo createRestrictedProfileWithThrow(
             @Nullable String name, @UserIdInt int parentUserId)
@@ -8504,7 +8499,6 @@
     }
 
     /**
-     * @hide
      * Checks whether to show a notification for sounds (e.g., alarms, timers, etc.) from
      * background users.
      */
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7f511e1..4860b7cd 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -239,6 +239,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.AccessibilityManagerInternal;
+import com.android.server.DockObserverInternal;
 import com.android.server.ExtconStateObserver;
 import com.android.server.ExtconUEventObserver;
 import com.android.server.GestureLauncherService;
@@ -473,6 +474,7 @@
     DisplayManager mDisplayManager;
     DisplayManagerInternal mDisplayManagerInternal;
     UserManagerInternal mUserManagerInternal;
+    DockObserverInternal mDockObserverInternal;
 
     private WallpaperManagerInternal mWallpaperManagerInternal;
 
@@ -2452,12 +2454,7 @@
         filter.addAction(UiModeManager.ACTION_ENTER_DESK_MODE);
         filter.addAction(UiModeManager.ACTION_EXIT_DESK_MODE);
         filter.addAction(Intent.ACTION_DOCK_EVENT);
-        Intent intent = mContext.registerReceiver(mDockReceiver, filter);
-        if (intent != null) {
-            // Retrieve current sticky dock event broadcast.
-            mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
-                    Intent.EXTRA_DOCK_STATE_UNDOCKED));
-        }
+        mContext.registerReceiver(mDockReceiver, filter);
 
         // register for multiuser-relevant broadcasts
         filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
@@ -3667,7 +3664,7 @@
                 }
                 break;
             case KeyEvent.KEYCODE_S:
-                if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
+                if (firstDown && event.isMetaPressed()) {
                     interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
                     notifyKeyGestureCompleted(event,
                             KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT);
@@ -3801,10 +3798,6 @@
                                 true /* leftOrTop */);
                         notifyKeyGestureCompleted(event,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT);
-                    } else if (event.isAltPressed()) {
-                        setSplitscreenFocus(true /* leftOrTop */);
-                        notifyKeyGestureCompleted(event,
-                                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT);
                     } else {
                         notifyKeyGestureCompleted(event,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
@@ -3821,11 +3814,6 @@
                         notifyKeyGestureCompleted(event,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT);
                         return true;
-                    } else if (event.isAltPressed()) {
-                        setSplitscreenFocus(false /* leftOrTop */);
-                        notifyKeyGestureCompleted(event,
-                                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT);
-                        return true;
                     }
                 }
                 break;
@@ -4241,9 +4229,7 @@
                     case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
@@ -4379,22 +4365,12 @@
                             true /* leftOrTop */);
                 }
                 return true;
-            case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
-                if (complete) {
-                    setSplitscreenFocus(true /* leftOrTop */);
-                }
-                return true;
             case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
                 if (complete) {
                     moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event),
                             false /* leftOrTop */);
                 }
                 return true;
-            case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
-                if (complete) {
-                    setSplitscreenFocus(false /* leftOrTop */);
-                }
-                return true;
             case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
                 if (complete) {
                     toggleKeyboardShortcutsMenu(deviceId);
@@ -5084,13 +5060,6 @@
         }
     }
 
-    private void setSplitscreenFocus(boolean leftOrTop) {
-        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
-        if (statusbar != null) {
-            statusbar.setSplitscreenFocus(leftOrTop);
-        }
-    }
-
     void launchHomeFromHotKey(int displayId) {
         launchHomeFromHotKey(displayId, true /* awakenFromDreams */, true /*respectKeyguard*/);
     }
@@ -6784,6 +6753,13 @@
             mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
         }
 
+        mDockObserverInternal = LocalServices.getService(DockObserverInternal.class);
+        if (mDockObserverInternal != null) {
+            // Get initial state from DockObserverInternal, DockObserver starts after WM.
+            int dockMode = mDockObserverInternal.getActualDockState();
+            mDefaultDisplayPolicy.setDockMode(dockMode);
+        }
+
         readCameraLensCoverState();
         updateUiMode();
         mDefaultDisplayRotation.updateOrientationListener();
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 977c6db..a5185a2 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -38,6 +38,7 @@
 import com.android.internal.os.MonotonicClock;
 import com.android.internal.os.PowerProfile;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.power.optimization.Flags;
 import com.android.server.power.stats.BatteryStatsImpl.BatteryStatsSession;
 
 import java.io.PrintWriter;
@@ -351,7 +352,7 @@
         accumulatedStats.endMonotonicTime = endMonotonicTime;
 
         accumulatedStats.builder.setStatsEndTimestamp(endWallClockTime);
-        accumulatedStats.builder.setStatsDuration(endWallClockTime - startMonotonicTime);
+        accumulatedStats.builder.setStatsDuration(endMonotonicTime - startMonotonicTime);
 
         mPowerAttributor.estimatePowerConsumption(accumulatedStats.builder, session.getHistory(),
                 startMonotonicTime, endMonotonicTime);
@@ -403,7 +404,10 @@
         }
         if ((query.getFlags()
                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) {
-            batteryUsageStatsBuilder.setBatteryHistory(session.getHistory().copy());
+            batteryUsageStatsBuilder.setBatteryHistory(session.getHistory().copy(),
+                    Flags.extendedBatteryHistoryContinuousCollectionEnabled()
+                            ? query.getPreferredHistoryDurationMs()
+                            : Long.MAX_VALUE);
         }
 
         mPowerAttributor.estimatePowerConsumption(batteryUsageStatsBuilder, session.getHistory(),
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 4827c9f..a80b1b2 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -56,12 +56,12 @@
     void toggleKeyboardShortcutsMenu(int deviceId);
 
     /**
-     * Used by InputMethodManagerService to notify the IME status.
+     * Sets the new IME window status.
      *
-     * @param displayId The display to which the IME is bound to.
-     * @param vis The IME visibility.
-     * @param backDisposition The IME back disposition.
-     * @param showImeSwitcher {@code true} when the IME switcher button should be shown.
+     * @param displayId The id of the display to which the IME is bound.
+     * @param vis The IME window visibility.
+     * @param backDisposition The IME back disposition mode.
+     * @param showImeSwitcher Whether the IME Switcher button should be shown.
      */
     void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
             @BackDispositionMode int backDisposition, boolean showImeSwitcher);
@@ -171,11 +171,11 @@
     void onProposedRotationChanged(int displayId, int rotation, boolean isValid);
 
     /**
-     * Notifies System UI that the display is ready to show system decorations.
+     * Notifies System UI that the system decorations should be added on the display.
      *
      * @param displayId display ID
      */
-    void onDisplayReady(int displayId);
+    void onDisplayAddSystemDecorations(int displayId);
 
     /**
      * Notifies System UI that the system decorations should be removed from the display.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 37056a4..c546388 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -87,7 +87,6 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
-import android.util.IntArray;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -125,7 +124,6 @@
 import com.android.server.power.ShutdownCheckPoints;
 import com.android.server.power.ShutdownThread;
 import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.systemui.shared.Flags;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -343,19 +341,15 @@
 
     @Override
     public void onDisplayAdded(int displayId) {
-        if (Flags.statusBarConnectedDisplays()) {
-            synchronized (mLock) {
-                mDisplayUiState.put(displayId, new UiState());
-            }
+        synchronized (mLock) {
+            mDisplayUiState.put(displayId, new UiState());
         }
     }
 
     @Override
     public void onDisplayRemoved(int displayId) {
-        if (Flags.statusBarConnectedDisplays()) {
-            synchronized (mLock) {
-                mDisplayUiState.remove(displayId);
-            }
+        synchronized (mLock) {
+            mDisplayUiState.remove(displayId);
         }
     }
 
@@ -776,10 +770,11 @@
         }
 
         @Override
-        public void onDisplayReady(int displayId) {
+        public void onDisplayAddSystemDecorations(int displayId) {
             if (isVisibleBackgroundUserOnDisplay(displayId)) {
                 if (SPEW) {
-                    Slog.d(TAG, "Skipping onDisplayReady for visible background user "
+                    Slog.d(TAG, "Skipping onDisplayAddSystemDecorations for visible background "
+                            + "user "
                             + mUserManagerInternal.getUserAssignedToDisplay(displayId));
                 }
                 return;
@@ -787,7 +782,7 @@
             IStatusBar bar = mBar;
             if (bar != null) {
                 try {
-                    bar.onDisplayReady(displayId);
+                    bar.onDisplayAddSystemDecorations(displayId);
                 } catch (RemoteException ex) {}
             }
         }
@@ -1366,66 +1361,53 @@
         return mTracingEnabled;
     }
 
+    // TODO(b/117478341): make it aware of multi-display if needed.
     @Override
     public void disable(int what, IBinder token, String pkg) {
         disableForUser(what, token, pkg, mCurrentUserId);
     }
 
-    /**
-     * Disable additional status bar features for user for all displays. Pass the bitwise-or of the
-     * {@code #DISABLE_*} flags. To re-enable everything, pass {@code #DISABLE_NONE}.
-     *
-     * Warning: Only pass {@code #DISABLE_*} flags into this function, do not use
-     * {@code #DISABLE2_*} flags.
-     */
+    // TODO(b/117478341): make it aware of multi-display if needed.
     @Override
     public void disableForUser(int what, IBinder token, String pkg, int userId) {
         enforceStatusBar();
         enforceValidCallingUser();
 
         synchronized (mLock) {
-            IntArray displayIds = new IntArray();
-            for (int i = 0; i < mDisplayUiState.size(); i++) {
-                displayIds.add(mDisplayUiState.keyAt(i));
-            }
-            disableLocked(displayIds, userId, what, token, pkg, 1);
+            disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1);
         }
     }
 
+    // TODO(b/117478341): make it aware of multi-display if needed.
     /**
-     * Disable additional status bar features. Pass the bitwise-or of the {@code #DISABLE2_*} flags.
-     * To re-enable everything, pass {@code #DISABLE2_NONE}.
+     * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
+     * To re-enable everything, pass {@link #DISABLE2_NONE}.
      *
-     * Warning: Only pass {@code #DISABLE2_*} flags into this function, do not use
-     * {@code #DISABLE_*} flags.
+     * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
      */
     @Override
     public void disable2(int what, IBinder token, String pkg) {
         disable2ForUser(what, token, pkg, mCurrentUserId);
     }
 
+    // TODO(b/117478341): make it aware of multi-display if needed.
     /**
-     * Disable additional status bar features for a given user for all displays. Pass the bitwise-or
-     * of the {@code #DISABLE2_*} flags. To re-enable everything, pass {@code #DISABLE2_NONE}.
+     * Disable additional status bar features for a given user. Pass the bitwise-or of the
+     * DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}.
      *
-     * Warning: Only pass {@code #DISABLE2_*} flags into this function, do not use
-     * {@code #DISABLE_*}  flags.
+     * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
      */
     @Override
     public void disable2ForUser(int what, IBinder token, String pkg, int userId) {
         enforceStatusBar();
 
         synchronized (mLock) {
-            IntArray displayIds = new IntArray();
-            for (int i = 0; i < mDisplayUiState.size(); i++) {
-                displayIds.add(mDisplayUiState.keyAt(i));
-            }
-            disableLocked(displayIds, userId, what, token, pkg, 2);
+            disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 2);
         }
     }
 
-    private void disableLocked(IntArray displayIds, int userId, int what, IBinder token,
-            String pkg, int whichFlag) {
+    private void disableLocked(int displayId, int userId, int what, IBinder token, String pkg,
+            int whichFlag) {
         // It's important that the the callback and the call to mBar get done
         // in the same order when multiple threads are calling this function
         // so they are paired correctly.  The messages on the handler will be
@@ -1435,27 +1417,18 @@
         // Ensure state for the current user is applied, even if passed a non-current user.
         final int net1 = gatherDisableActionsLocked(mCurrentUserId, 1);
         final int net2 = gatherDisableActionsLocked(mCurrentUserId, 2);
-        boolean shouldCallNotificationOnSetDisabled = false;
-        IStatusBar bar = mBar;
-        for (int displayId : displayIds.toArray()) {
-            final UiState state = getUiState(displayId);
-            if (!state.disableEquals(net1, net2)) {
-                shouldCallNotificationOnSetDisabled = true;
-                state.setDisabled(net1, net2);
-                if (bar != null) {
-                    try {
-                        // TODO(b/388244660): Create IStatusBar#disableForAllDisplays to avoid
-                        // multiple IPC calls.
-                        bar.disable(displayId, net1, net2);
-                    } catch (RemoteException ex) {
-                        Slog.e(TAG, "Unable to disable Status bar.", ex);
-                    }
+        final UiState state = getUiState(displayId);
+        if (!state.disableEquals(net1, net2)) {
+            state.setDisabled(net1, net2);
+            mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
+            IStatusBar bar = mBar;
+            if (bar != null) {
+                try {
+                    bar.disable(displayId, net1, net2);
+                } catch (RemoteException ex) {
                 }
             }
         }
-        if (shouldCallNotificationOnSetDisabled) {
-            mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
-        }
     }
 
     /**
@@ -1610,8 +1583,7 @@
         if (SPEW) Slog.d(TAG, "setDisableFlags(0x" + Integer.toHexString(flags) + ")");
 
         synchronized (mLock) {
-            disableLocked(IntArray.wrap(new int[]{displayId}), mCurrentUserId, flags,
-                    mSysUiVisToken, cause, 1);
+            disableLocked(displayId, mCurrentUserId, flags, mSysUiVisToken, cause, 1);
         }
     }
 
@@ -2241,33 +2213,6 @@
         }
     }
 
-    /**
-     *  Called when the notification should be unbundled.
-     * @param key the notification key
-     */
-    @Override
-    public void unbundleNotification(@Nullable String key) {
-        enforceStatusBarService();
-        enforceValidCallingUser();
-        Binder.withCleanCallingIdentity(() -> {
-            mNotificationDelegate.unbundleNotification(key);
-        });
-    }
-
-    /**
-     *  Called when the notification should be rebundled.
-     * @param key the notification key
-     */
-    @Override
-    public void rebundleNotification(String key) {
-        enforceStatusBarService();
-        enforceValidCallingUser();
-        Binder.withCleanCallingIdentity(() -> {
-            mNotificationDelegate.rebundleNotification(key);
-        });
-    }
-
-
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
             String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
index 25c0750..f413fe3 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
@@ -26,7 +26,10 @@
     /**
      * Notifies the display is ready for adding wallpaper on it.
      */
-    public abstract void onDisplayReady(int displayId);
+    public abstract void onDisplayAddSystemDecorations(int displayId);
+
+    /** Notifies when display stop showing system decorations and wallpaper. */
+    public abstract void onDisplayRemoveSystemDecorations(int displayId);
 
     /** Notifies when the screen finished turning on and is visible to the user. */
     public abstract void onScreenTurnedOn(int displayId);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index a8deeea..09b1073 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -118,6 +118,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.IntArray;
 import android.util.Slog;
@@ -160,6 +161,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -665,71 +667,7 @@
 
         @Override
         public void onDisplayRemoved(int displayId) {
-            synchronized (mLock) {
-                if (enableConnectedDisplaysWallpaper()) {
-                    // There could be at most 2 wallpaper connections per display:
-                    // 1. system & lock are the same: mLastWallpaper
-                    // 2. system, lock are different: mLastWallpaper, mLastLockWallpaper
-                    // 3. fallback used as both system & lock wallpaper: mFallbackWallpaper
-                    // 4. fallback used as lock only wallpaper: mFallbackWallpaper,
-                    //    mLastWallpaper
-                    // 5. fallback used as system only wallpaper: mFallbackWallpaper,
-                    //    mLastLockWallpaper
-                    List<WallpaperData> pendingDisconnectWallpapers = new ArrayList<>();
-                    if (mLastWallpaper != null && mLastWallpaper.connection != null
-                            && mLastWallpaper.connection.containsDisplay(displayId)) {
-                        pendingDisconnectWallpapers.add(mLastWallpaper);
-                    }
-                    if (mLastLockWallpaper != null && mLastLockWallpaper.connection != null
-                            && mLastLockWallpaper.connection.containsDisplay(displayId)) {
-                        pendingDisconnectWallpapers.add(mLastLockWallpaper);
-                    }
-                    if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null
-                            && mFallbackWallpaper.connection.containsDisplay(displayId)) {
-                        pendingDisconnectWallpapers.add(mFallbackWallpaper);
-                    }
-                    for (int i = 0; i < pendingDisconnectWallpapers.size(); i++) {
-                        WallpaperData wallpaper = pendingDisconnectWallpapers.get(i);
-                        DisplayConnector displayConnector =
-                                wallpaper.connection.getDisplayConnectorOrCreate(displayId);
-                        if (displayConnector == null) {
-                            Slog.w(TAG,
-                                    "Fail to disconnect wallpaper upon display removal");
-                            return;
-                        }
-                        displayConnector.disconnectLocked(wallpaper.connection);
-                        wallpaper.connection.removeDisplayConnector(displayId);
-                    }
-                } else {
-                    if (mLastWallpaper != null) {
-                        WallpaperData targetWallpaper = null;
-                        if (mLastWallpaper.connection != null
-                                && mLastWallpaper.connection.containsDisplay(displayId)) {
-                            targetWallpaper = mLastWallpaper;
-                        } else if (mFallbackWallpaper != null
-                                && mFallbackWallpaper.connection != null
-                                && mFallbackWallpaper.connection.containsDisplay(
-                                displayId)) {
-                            targetWallpaper = mFallbackWallpaper;
-                        }
-                        if (targetWallpaper == null) return;
-                        DisplayConnector connector =
-                                targetWallpaper.connection.getDisplayConnectorOrCreate(
-                                        displayId);
-                        if (connector == null) return;
-                        connector.disconnectLocked(targetWallpaper.connection);
-                        targetWallpaper.connection.removeDisplayConnector(displayId);
-                    }
-                }
-
-                mWallpaperDisplayHelper.removeDisplayData(displayId);
-
-                for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) {
-                    final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks =
-                            mColorsChangedListeners.valueAt(i);
-                    callbacks.delete(displayId);
-                }
-            }
+            onDisplayRemovedInternal(displayId);
         }
 
         @Override
@@ -772,6 +710,12 @@
     private final ComponentName mImageWallpaper;
 
     /**
+     * Name of the component that is used when the user-selected wallpaper is incompatible with the
+     * display's resolution or aspect ratio.
+     */
+    @Nullable private final ComponentName mFallbackWallpaperComponent;
+
+    /**
      * Default image wallpaper shall never changed after system service started, caching it when we
      * first read the image file.
      */
@@ -799,12 +743,38 @@
     final WallpaperDisplayHelper mWallpaperDisplayHelper;
     final WallpaperCropper mWallpaperCropper;
 
-    private boolean supportsMultiDisplay(WallpaperConnection connection) {
-        if (connection != null) {
-            return connection.mInfo == null // This is image wallpaper
-                    || connection.mInfo.supportsMultipleDisplays();
+    // TODO(b/384519749): Remove this set after we introduce the aspect ratio check.
+    private final Set<Integer> mWallpaperCompatibleDisplaysForTest = new ArraySet<>();
+
+    private boolean isWallpaperCompatibleForDisplay(int displayId, WallpaperConnection connection) {
+        if (connection == null) {
+            return false;
         }
-        return false;
+        // Non image wallpaper.
+        if (connection.mInfo != null) {
+            return connection.mInfo.supportsMultipleDisplays();
+        }
+
+        // Image wallpaper
+        if (enableConnectedDisplaysWallpaper()) {
+            // TODO(b/384519749): check display's resolution and image wallpaper cropped image
+            //  aspect ratio.
+            return displayId == DEFAULT_DISPLAY
+                    || mWallpaperCompatibleDisplaysForTest.contains(displayId);
+        }
+        // When enableConnectedDisplaysWallpaper is off, we assume the image wallpaper supports all
+        // usable displays.
+        return true;
+    }
+
+    @VisibleForTesting
+    void addWallpaperCompatibleDisplayForTest(int displayId) {
+        mWallpaperCompatibleDisplaysForTest.add(displayId);
+    }
+
+    @VisibleForTesting
+    void removeWallpaperCompatibleDisplayForTest(int displayId) {
+        mWallpaperCompatibleDisplaysForTest.remove(displayId);
     }
 
     private void updateFallbackConnection() {
@@ -815,7 +785,9 @@
             Slog.w(TAG, "Fallback wallpaper connection has not been created yet!!");
             return;
         }
-        if (supportsMultiDisplay(systemConnection)) {
+        // TODO(b/384520326) Passing DEFAULT_DISPLAY temporarily before we revamp the
+        //  multi-display supports.
+        if (isWallpaperCompatibleForDisplay(DEFAULT_DISPLAY, systemConnection)) {
             if (fallbackConnection.mDisplayConnector.size() != 0) {
                 fallbackConnection.forEachDisplayConnector(connector -> {
                     if (connector.mEngine != null) {
@@ -990,16 +962,14 @@
         private void initDisplayState() {
             // Do not initialize fallback wallpaper
             if (!mWallpaper.equals(mFallbackWallpaper)) {
-                if (supportsMultiDisplay(this)) {
-                    // The system wallpaper is image wallpaper or it can supports multiple displays.
-                    appendConnectorWithCondition(display ->
-                            mWallpaperDisplayHelper.isUsableDisplay(display, mClientUid));
-                } else {
-                    // The system wallpaper does not support multiple displays, so just attach it on
-                    // default display.
-                    mDisplayConnector.append(DEFAULT_DISPLAY,
-                            new DisplayConnector(DEFAULT_DISPLAY));
-                }
+                appendConnectorWithCondition(display -> {
+                    final int displayId = display.getDisplayId();
+                    if (display.getDisplayId() == DEFAULT_DISPLAY) {
+                        return true;
+                    }
+                    return mWallpaperDisplayHelper.isUsableDisplay(display, mClientUid)
+                            && isWallpaperCompatibleForDisplay(displayId, /* connection= */ this);
+                });
             }
         }
 
@@ -1601,6 +1571,12 @@
         mShuttingDown = false;
         mImageWallpaper = ComponentName.unflattenFromString(
                 context.getResources().getString(R.string.image_wallpaper_component));
+        if (enableConnectedDisplaysWallpaper()) {
+            mFallbackWallpaperComponent = ComponentName.unflattenFromString(
+                    context.getResources().getString(R.string.fallback_wallpaper_component));
+        } else {
+            mFallbackWallpaperComponent = null;
+        }
         ComponentName defaultComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
         mDefaultWallpaperComponent = defaultComponent == null ? mImageWallpaper : defaultComponent;
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
@@ -1660,8 +1636,15 @@
 
     private final class LocalService extends WallpaperManagerInternal {
         @Override
-        public void onDisplayReady(int displayId) {
-            onDisplayReadyInternal(displayId);
+        public void onDisplayAddSystemDecorations(int displayId) {
+            onDisplayAddSystemDecorationsInternal(displayId);
+        }
+
+        @Override
+        public void onDisplayRemoveSystemDecorations(int displayId) {
+            // The display mirroring starts. The handling logic is the same as when removing a
+            // display.
+            onDisplayRemovedInternal(displayId);
         }
 
         @Override
@@ -3613,6 +3596,13 @@
             if (componentName != null && !componentName.equals(mImageWallpaper)) {
                 // The requested component is not the static wallpaper service, so make sure it's
                 // actually a wallpaper service.
+                if (mFallbackWallpaperComponent != null
+                        && componentName.equals(mFallbackWallpaperComponent)) {
+                    // The fallback wallpaper does not declare WallpaperService.SERVICE_INTERFACE
+                    // action in its intent filter to prevent it from being listed in the wallpaper
+                    // picker. And thus, use explicit intent to query the metadata.
+                    intent = new Intent().setComponent(mFallbackWallpaperComponent);
+                }
                 List<ResolveInfo> ris =
                         mIPackageManager.queryIntentServices(intent,
                                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
@@ -3954,7 +3944,7 @@
         return (wallpaper != null) ? wallpaper.allowBackup : false;
     }
 
-    private void onDisplayReadyInternal(int displayId) {
+    private void onDisplayAddSystemDecorationsInternal(int displayId) {
         synchronized (mLock) {
             if (mLastWallpaper == null) {
                 return;
@@ -3971,7 +3961,7 @@
 
                 for (int i = 0; i < wallpapers.size(); i++) {
                     WallpaperData wallpaper = wallpapers.get(i);
-                    if (supportsMultiDisplay(wallpaper.connection)) {
+                    if (isWallpaperCompatibleForDisplay(displayId, wallpaper.connection)) {
                         final DisplayConnector connector =
                                 wallpaper.connection.getDisplayConnectorOrCreate(displayId);
                         if (connector != null) {
@@ -3993,7 +3983,7 @@
                 }
                 mFallbackWallpaper.mWhich = useFallbackWallpaperWhich;
             } else {
-                if (supportsMultiDisplay(mLastWallpaper.connection)) {
+                if (isWallpaperCompatibleForDisplay(displayId, mLastWallpaper.connection)) {
                     final DisplayConnector connector =
                             mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
                     if (connector == null) return;
@@ -4016,6 +4006,78 @@
         }
     }
 
+    // This method may be called even if the display is not being removed from the system.
+    // This can be called when the display is removed, or when the display system decorations are
+    // removed to start mirroring.
+    private void onDisplayRemovedInternal(int displayId) {
+        synchronized (mLock) {
+            if (enableConnectedDisplaysWallpaper()) {
+                // There could be at most 2 wallpaper connections per display:
+                // 1. system & lock are the same: mLastWallpaper
+                // 2. system, lock are different: mLastWallpaper, mLastLockWallpaper
+                // 3. fallback used as both system & lock wallpaper: mFallbackWallpaper
+                // 4. fallback used as lock only wallpaper: mFallbackWallpaper,
+                //    mLastWallpaper
+                // 5. fallback used as system only wallpaper: mFallbackWallpaper,
+                //    mLastLockWallpaper
+                List<WallpaperData> pendingDisconnectWallpapers = new ArrayList<>();
+                if (mLastWallpaper != null && mLastWallpaper.connection != null
+                        && mLastWallpaper.connection.containsDisplay(displayId)) {
+                    pendingDisconnectWallpapers.add(mLastWallpaper);
+                }
+                if (mLastLockWallpaper != null && mLastLockWallpaper.connection != null
+                        && mLastLockWallpaper.connection.containsDisplay(displayId)) {
+                    pendingDisconnectWallpapers.add(mLastLockWallpaper);
+                }
+                if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null
+                        && mFallbackWallpaper.connection.containsDisplay(displayId)) {
+                    pendingDisconnectWallpapers.add(mFallbackWallpaper);
+                }
+                for (int i = 0; i < pendingDisconnectWallpapers.size(); i++) {
+                    WallpaperData wallpaper = pendingDisconnectWallpapers.get(i);
+                    DisplayConnector displayConnector =
+                            wallpaper.connection.getDisplayConnectorOrCreate(displayId);
+                    if (displayConnector == null) {
+                        Slog.w(TAG,
+                                "Fail to disconnect wallpaper upon display removes system "
+                                        + "decorations");
+                        return;
+                    }
+                    displayConnector.disconnectLocked(wallpaper.connection);
+                    wallpaper.connection.removeDisplayConnector(displayId);
+                }
+            } else {
+                if (mLastWallpaper != null) {
+                    WallpaperData targetWallpaper = null;
+                    if (mLastWallpaper.connection != null
+                            && mLastWallpaper.connection.containsDisplay(displayId)) {
+                        targetWallpaper = mLastWallpaper;
+                    } else if (mFallbackWallpaper != null
+                            && mFallbackWallpaper.connection != null
+                            && mFallbackWallpaper.connection.containsDisplay(
+                            displayId)) {
+                        targetWallpaper = mFallbackWallpaper;
+                    }
+                    if (targetWallpaper == null) return;
+                    DisplayConnector connector =
+                            targetWallpaper.connection.getDisplayConnectorOrCreate(
+                                    displayId);
+                    if (connector == null) return;
+                    connector.disconnectLocked(targetWallpaper.connection);
+                    targetWallpaper.connection.removeDisplayConnector(displayId);
+                }
+            }
+
+            mWallpaperDisplayHelper.removeDisplayData(displayId);
+
+            for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) {
+                final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks =
+                        mColorsChangedListeners.valueAt(i);
+                callbacks.delete(displayId);
+            }
+        }
+    }
+
     void saveSettingsLocked(int userId) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
         t.traceBegin("WPMS.saveSettingsLocked-" + userId);
@@ -4100,8 +4162,14 @@
             mFallbackWallpaper.allowBackup = false;
             mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
             mFallbackWallpaper.mBindSource = BindSource.INITIALIZE_FALLBACK;
-            bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
-                    mFallbackWallpaper, null);
+            if (mFallbackWallpaperComponent == null) {
+                bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
+                        mFallbackWallpaper, null);
+            } else {
+                mFallbackWallpaper.mWhich = FLAG_SYSTEM | FLAG_LOCK;
+                bindWallpaperComponentLocked(mFallbackWallpaperComponent, true, false,
+                        mFallbackWallpaper, null);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1d12c56..89b46bc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -46,6 +46,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.isFloating;
 import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
 import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
 import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
@@ -3231,8 +3232,8 @@
      * Returns {@code true} if the fixed orientation, aspect ratio, resizability of the application
      * can be ignored.
      */
-    static boolean canBeUniversalResizeable(ApplicationInfo appInfo, WindowManagerService wms,
-            boolean isLargeScreen, boolean forActivity) {
+    static boolean canBeUniversalResizeable(@NonNull ApplicationInfo appInfo,
+            WindowManagerService wms, boolean isLargeScreen, boolean forActivity) {
         if (appInfo.category == ApplicationInfo.CATEGORY_GAME) {
             return false;
         }
@@ -8376,6 +8377,7 @@
         mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
         getResolvedOverrideConfiguration().seq = mConfigurationSeq;
 
+        // TODO(b/392069771): Move to AppCompatSandboxingPolicy.
         // Sandbox max bounds by setting it to the activity bounds, if activity is letterboxed, or
         // has or will have mAppCompatDisplayInsets for size compat. Also forces an activity to be
         // sandboxed or not depending upon the configuration settings.
@@ -8404,6 +8406,20 @@
             resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
         }
 
+        // Sandbox activity bounds in freeform to app bounds to force app to display within the
+        // container. This prevents UI cropping when activities can draw below insets which are
+        // normally excluded from appBounds before targetSDK < 35
+        // (see ConfigurationContainer#applySizeOverrideIfNeeded).
+        if (isFloating(parentWindowingMode)) {
+            Rect appBounds = resolvedConfig.windowConfiguration.getAppBounds();
+            if (appBounds == null || appBounds.isEmpty()) {
+                // When there is no override bounds, the activity will inherit the bounds from
+                // parent.
+                appBounds = mResolveConfigHint.mParentAppBoundsOverride;
+            }
+            resolvedConfig.windowConfiguration.setBounds(appBounds);
+        }
+
         applySizeOverrideIfNeeded(
                 mDisplayContent,
                 info.applicationInfo,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c7d4467..6f76618 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -3655,7 +3655,7 @@
     private static String getIntentRedirectPreventedLogMessage(@NonNull String message,
             @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
             int callingUid, @Nullable String callingPackage) {
-        return "[IntentRedirect]" + message + " intentCreatorUid: " + intentCreatorUid
+        return "[IntentRedirect Hardening] " + message + " intentCreatorUid: " + intentCreatorUid
                 + "; intentCreatorPackage: " + intentCreatorPackage + "; callingUid: " + callingUid
                 + "; callingPackage: " + callingPackage + "; intent: " + intent;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d4f9c090..ddb9f17 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2154,6 +2154,16 @@
         }
     }
 
+    /**
+     * @return ehether the application could be universal resizeable on a large screen,
+     * ignoring any overrides
+     */
+    @Override
+    public boolean canBeUniversalResizeable(@NonNull ApplicationInfo appInfo) {
+        return ActivityRecord.canBeUniversalResizeable(appInfo, mWindowManager,
+                /* isLargeScreen */ true, /* forActivity */ false);
+    }
+
     @Override
     public void removeAllVisibleRecentTasks() {
         mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
@@ -2978,37 +2988,44 @@
             throw new SecurityException("Requires permission "
                     + android.Manifest.permission.DEVICE_POWER);
         }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                setLockScreenShownLocked(keyguardShowing, aodShowing);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
 
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            if (mKeyguardShown != keyguardShowing) {
-                mKeyguardShown = keyguardShowing;
-                final Message msg = PooledLambda.obtainMessage(
-                        ActivityManagerInternal::reportCurKeyguardUsageEvent, mAmInternal,
-                        keyguardShowing);
-                mH.sendMessage(msg);
+    @GuardedBy("mGlobalLock")
+    void setLockScreenShownLocked(boolean keyguardShowing, boolean aodShowing) {
+        if (mKeyguardShown != keyguardShowing) {
+            mKeyguardShown = keyguardShowing;
+            final Message msg = PooledLambda.obtainMessage(
+                    ActivityManagerInternal::reportCurKeyguardUsageEvent, mAmInternal,
+                    keyguardShowing);
+            mH.sendMessage(msg);
+        }
+        // Always reset the state regardless of keyguard-showing change, because that means the
+        // unlock is either completed or canceled.
+        if ((mDemoteTopAppReasons & DEMOTE_TOP_REASON_DURING_UNLOCKING) != 0) {
+            mDemoteTopAppReasons &= ~DEMOTE_TOP_REASON_DURING_UNLOCKING;
+            // The scheduling group of top process was demoted by unlocking, so recompute
+            // to restore its real top priority if possible.
+            if (mTopApp != null) {
+                mTopApp.scheduleUpdateOomAdj();
             }
-            // Always reset the state regardless of keyguard-showing change, because that means the
-            // unlock is either completed or canceled.
-            if ((mDemoteTopAppReasons & DEMOTE_TOP_REASON_DURING_UNLOCKING) != 0) {
-                mDemoteTopAppReasons &= ~DEMOTE_TOP_REASON_DURING_UNLOCKING;
-                // The scheduling group of top process was demoted by unlocking, so recompute
-                // to restore its real top priority if possible.
-                if (mTopApp != null) {
-                    mTopApp.scheduleUpdateOomAdj();
-                }
-            }
-            try {
-                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "setLockScreenShown");
-                mRootWindowContainer.forAllDisplays(displayContent -> {
-                    mKeyguardController.setKeyguardShown(displayContent.getDisplayId(),
-                            keyguardShowing, aodShowing);
-                });
-                maybeHideLockedProfileActivityLocked();
-            } finally {
-                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-                Binder.restoreCallingIdentity(ident);
-            }
+        }
+        try {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "setLockScreenShown");
+            mRootWindowContainer.forAllDisplays(displayContent -> {
+                mKeyguardController.setKeyguardShown(displayContent.getDisplayId(),
+                        keyguardShowing, aodShowing);
+            });
+            maybeHideLockedProfileActivityLocked();
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
         mH.post(() -> {
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index ab1778a..15c0789 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -295,9 +295,14 @@
                 // {@link ActivityRecord#shouldCreateAppCompatDisplayInsets()} will be false for
                 // both activities that are naturally resizeable and activities that have been
                 // forced resizeable.
+                // Camera compat mode is an exception to this, where the activity is letterboxed
+                // to an aspect ratio commonly found on phones, e.g. 16:9, to avoid issues like
+                // stretching of the camera preview.
                 || (Flags.ignoreAspectRatioRestrictionsForResizeableFreeformActivities()
                     && task.getWindowingMode() == WINDOWING_MODE_FREEFORM
-                    && !mActivityRecord.shouldCreateAppCompatDisplayInsets())) {
+                    && !mActivityRecord.shouldCreateAppCompatDisplayInsets()
+                    && !AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio(
+                            mActivityRecord))) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index 35fa39d..d994a19 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -180,10 +180,7 @@
                 return true;
             }
 
-            final AppCompatCameraPolicy cameraPolicy = AppCompatCameraPolicy
-                    .getAppCompatCameraPolicy(mActivityRecord);
-            if (cameraPolicy != null
-                    && cameraPolicy.isTreatmentEnabledForActivity(mActivityRecord)) {
+            if (AppCompatCameraPolicy.isTreatmentEnabledForActivity(mActivityRecord)) {
                 Slog.w(TAG, "Ignoring orientation update to "
                         + screenOrientationToString(requestedOrientation)
                         + " due to camera compat treatment for " + mActivityRecord);
diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java
index 6bf1c46..e3906f9 100644
--- a/services/core/java/com/android/server/wm/DesktopModeHelper.java
+++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java
@@ -23,6 +23,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
 
 /**
  * Constants for desktop mode feature
@@ -35,7 +36,7 @@
             "persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
 
     /** Whether desktop mode is enabled. */
-    static boolean isDesktopModeEnabled() {
+    private static boolean isDesktopModeEnabled() {
         return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue();
     }
 
@@ -56,11 +57,30 @@
         return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported);
     }
 
+    static boolean isDesktopModeDevOptionsSupported(@NonNull Context context) {
+        return context.getResources().getBoolean(R.bool.config_isDesktopModeDevOptionSupported);
+    }
+
+    /**
+     * Check if Desktop mode should be enabled because the dev option is shown and enabled.
+     */
+    private static boolean isDesktopModeEnabledByDevOption(@NonNull Context context) {
+        return DesktopModeFlags.isDesktopModeForcedEnabled() && (isDesktopModeDevOptionsSupported(
+                context) || isDeviceEligibleForDesktopMode(context));
+    }
+
+    @VisibleForTesting
+    static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
+        return !shouldEnforceDeviceRestrictions() || isDesktopModeSupported(context)  || (
+                Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionsSupported(
+                        context));
+    }
+
     /**
      * Return {@code true} if desktop mode can be entered on the current device.
      */
     static boolean canEnterDesktopMode(@NonNull Context context) {
-        return isDesktopModeEnabled()
-                && (!shouldEnforceDeviceRestrictions() || isDesktopModeSupported(context));
+        return (isDesktopModeEnabled() && isDeviceEligibleForDesktopMode(context))
+                || isDesktopModeEnabledByDevOption(context);
     }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 4b30a43..9d9c5ce 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -164,7 +165,8 @@
                                 FEATURE_APP_ZOOM_OUT)
                                 .all()
                                 .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL,
-                                        TYPE_STATUS_BAR, TYPE_NOTIFICATION_SHADE, TYPE_WALLPAPER)
+                                        TYPE_STATUS_BAR, TYPE_NOTIFICATION_SHADE,
+                                        TYPE_KEYGUARD_DIALOG, TYPE_WALLPAPER)
                                 .build());
             }
             if (USE_DISPLAY_AREA_FOR_FULLSCREEN_MAGNIFICATION) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e1a50a9..5329e3b 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1887,17 +1887,17 @@
         mCanSystemBarsBeShownByUser = canBeShown;
     }
 
-    void notifyDisplayReady() {
+    void notifyDisplayAddSystemDecorations() {
         mHandler.post(() -> {
             final int displayId = getDisplayId();
             StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
             if (statusBar != null) {
-                statusBar.onDisplayReady(displayId);
+                statusBar.onDisplayAddSystemDecorations(displayId);
             }
             final WallpaperManagerInternal wpMgr = LocalServices
                     .getService(WallpaperManagerInternal.class);
             if (wpMgr != null) {
-                wpMgr.onDisplayReady(displayId);
+                wpMgr.onDisplayAddSystemDecorations(displayId);
             }
         });
     }
@@ -1910,6 +1910,11 @@
                     if (statusBar != null) {
                         statusBar.onDisplayRemoveSystemDecorations(displayId);
                     }
+                    final WallpaperManagerInternal wpMgr =
+                            LocalServices.getService(WallpaperManagerInternal.class);
+                    if (wpMgr != null) {
+                        wpMgr.onDisplayRemoveSystemDecorations(displayId);
+                    }
                 });
     }
 
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index bcfaa39..59ca79c 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -361,7 +361,7 @@
             controlTarget = mDisplayContent.getImeHostOrFallback(
                     ((InsetsControlTarget) imeInsetsTarget).getWindow());
 
-            if (controlTarget != imeInsetsTarget) {
+            if (controlTarget != null && controlTarget != imeInsetsTarget) {
                 ImeTracker.forLogging().onProgress(statsToken,
                         ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
                 controlTarget.setImeInputTargetRequestedVisibility(imeVisible, statsToken);
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index f465c95..4bcba13 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -899,9 +899,7 @@
         }
 
         @Override
-        public void notifyAnimationRunningStateChanged(boolean running,
-                @InsetsController.AnimationType int animationType,
-                @InsetsType int insetsTypes) {
+        public void notifyAnimationRunningStateChanged(boolean running) {
             mInsetsAnimationRunning = running;
         }
     }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e2b5c83..cf464c7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -138,6 +138,7 @@
 import android.view.DisplayInfo;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
+import android.window.DesktopModeFlags;
 import android.window.TaskFragmentAnimationParams;
 import android.window.WindowContainerToken;
 
@@ -1908,7 +1909,7 @@
         // appropriate.
         removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
 
-        if (Flags.enableTopVisibleRootTaskPerUserTracking()) {
+        if (DesktopModeFlags.ENABLE_TOP_VISIBLE_ROOT_TASK_PER_USER_TRACKING.isTrue()) {
             final IntArray visibleRootTasks = new IntArray();
             forAllRootTasks(rootTask -> {
                 if ((mCurrentUser == rootTask.mUserId || rootTask.showForAllUsers())
@@ -2844,7 +2845,13 @@
         }
 
         startHomeOnDisplay(mCurrentUser, reason, displayContent.getDisplayId());
-        displayContent.getDisplayPolicy().notifyDisplayReady();
+        if (enableDisplayContentModeManagement()) {
+            if (displayContent.isSystemDecorationsSupported()) {
+                displayContent.getDisplayPolicy().notifyDisplayAddSystemDecorations();
+            }
+        } else {
+            displayContent.getDisplayPolicy().notifyDisplayAddSystemDecorations();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 37cc0d2..27683b2 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1504,16 +1504,15 @@
         }
 
         // Update the input-sink (touch-blocking) state now that the animation is finished.
-        SurfaceControl.Transaction inputSinkTransaction = null;
+        boolean scheduleAnimation = false;
         for (int i = 0; i < mParticipants.size(); ++i) {
             final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
             if (ar == null || !ar.isVisible() || ar.getParent() == null) continue;
-            if (inputSinkTransaction == null) {
-                inputSinkTransaction = ar.mWmService.mTransactionFactory.get();
-            }
-            ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(inputSinkTransaction);
+            scheduleAnimation = true;
+            ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(ar.getPendingTransaction());
         }
-        if (inputSinkTransaction != null) inputSinkTransaction.apply();
+        // To apply pending transactions.
+        if (scheduleAnimation) mController.mAtm.mWindowManager.scheduleAnimationLocked();
 
         // Always schedule stop processing when transition finishes because activities don't
         // stop while they are in a transition thus their stop could still be pending.
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e761e02..883d8f9 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1680,26 +1680,27 @@
      * Sets the specified orientation of this container. It percolates this change upward along the
      * hierarchy to let each level of the hierarchy a chance to respond to it.
      *
-     * @param orientation the specified orientation. Needs to be one of {@link ScreenOrientation}.
+     * @param requestedOrientation the specified orientation. Needs to be one of
+     *                             {@link ScreenOrientation}.
      * @param requestingContainer the container which orientation request has changed. Mostly used
      *                            to ensure it gets correct configuration.
      * @return the resolved override orientation of this window container.
      */
     @ScreenOrientation
-    int setOrientation(@ScreenOrientation int orientation,
+    int setOrientation(@ScreenOrientation int requestedOrientation,
             @Nullable WindowContainer requestingContainer) {
-        if (getOverrideOrientation() == orientation) {
-            return orientation;
+        if (getOverrideOrientation() == requestedOrientation) {
+            return requestedOrientation;
         }
-        setOverrideOrientation(orientation);
+        setOverrideOrientation(requestedOrientation);
         final WindowContainer parent = getParent();
         if (parent == null) {
-            return orientation;
+            return requestedOrientation;
         }
         // The derived class can return a result that is different from the given orientation.
-        final int resolvedOrientation = getOverrideOrientation();
+        final int actualOverrideOrientation = getOverrideOrientation();
         if (getConfiguration().orientation != getRequestedConfigurationOrientation(
-                false /* forDisplay */, resolvedOrientation)
+                false /* forDisplay */, actualOverrideOrientation)
                 // Update configuration directly only if the change won't be dispatched from
                 // ancestor. This prevents from computing intermediate configuration when the
                 // parent also needs to be updated from the ancestor. E.g. the app requests
@@ -1707,12 +1708,12 @@
                 // the task can be updated to portrait first so the configuration can be
                 // computed in a consistent environment.
                 && (inMultiWindowMode()
-                        || !handlesOrientationChangeFromDescendant(orientation))) {
+                        || !handlesOrientationChangeFromDescendant(requestedOrientation))) {
             // Resolve the requested orientation.
             onConfigurationChanged(parent.getConfiguration());
         }
         onDescendantOrientationChanged(requestingContainer);
-        return resolvedOrientation;
+        return actualOverrideOrientation;
     }
 
     @ScreenOrientation
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 7a8230f..c77b1d9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -41,7 +41,6 @@
 import android.view.IInputFilter;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IWindow;
-import android.view.InsetsController;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationTarget;
 import android.view.Surface;
@@ -470,24 +469,6 @@
     public abstract void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion);
 
     /**
-     * Set by the autofill service to observe changes in the ime animations.
-     *
-     * @param listener The callbacks to invoke.
-     */
-    public abstract void setImeInsetsAnimationChangeListener(
-            @Nullable ImeInsetsAnimationChangeListener listener);
-
-    /** Listener for changes in ime insets animation */
-    public interface ImeInsetsAnimationChangeListener {
-
-        /** Notify on start of animation */
-        void onAnimationStart(@InsetsController.AnimationType int animationType, int userId);
-
-        /** Notify on end of animation */
-        void onAnimationEnd(@InsetsController.AnimationType int animationType, int userId);
-    }
-
-    /**
      * Sets a callback for observing which windows are touchable for the purposes
      * of accessibility on specified display.
      *
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 28ea3b0..3a1d652 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.service.autofill.Flags.improveFillDialogAconfig;
 import static android.Manifest.permission.ACCESS_SURFACE_FLINGER;
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
 import static android.Manifest.permission.INPUT_CONSUMER;
@@ -278,7 +277,6 @@
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputWindowHandle;
-import android.view.InsetsController;
 import android.view.InsetsFrameProvider;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
@@ -795,9 +793,6 @@
     final TrustedPresentationListenerController mTrustedPresentationListenerController =
             new TrustedPresentationListenerController();
 
-    private WindowManagerInternal.ImeInsetsAnimationChangeListener
-            mImeInsetsAnimationChangeListener;
-
     @VisibleForTesting
     final class SettingsObserver extends ContentObserver {
         private final Uri mDisplayInversionEnabledUri =
@@ -8627,14 +8622,6 @@
             // WMS.takeAssistScreenshot takes care of the locking.
             return WindowManagerService.this.takeAssistScreenshot(windowTypesToExclude);
         }
-
-        @Override
-        public void setImeInsetsAnimationChangeListener(
-                @Nullable WindowManagerInternal.ImeInsetsAnimationChangeListener listener) {
-            synchronized (mGlobalLock) {
-                mImeInsetsAnimationChangeListener = listener;
-            }
-        }
     }
 
     private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {
@@ -10192,24 +10179,6 @@
         }
     }
 
-    @Override
-    public void notifyImeInsetsAnimationStateChanged(
-            boolean running, @InsetsController.AnimationType int animationType) {
-        if (improveFillDialogAconfig()) {
-            synchronized (mGlobalLock) {
-                if (mImeInsetsAnimationChangeListener == null) {
-                    return;
-                }
-                if (running) {
-                    mImeInsetsAnimationChangeListener.onAnimationStart(
-                            animationType, mCurrentUserId);
-                } else {
-                    mImeInsetsAnimationChangeListener.onAnimationEnd(animationType, mCurrentUserId);
-                }
-            }
-        }
-    }
-
     boolean getDisableSecureWindows() {
         return mDisableSecureWindows;
     }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a1755e4d..b4c2c01 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -756,40 +756,6 @@
                             t.getTaskFragmentOrganizer());
                 }
             }
-            // Queue-up bounds-change transactions for tasks which are now organized. Do
-            // this after hierarchy ops so we have the final organized state.
-            entries = t.getChanges().entrySet().iterator();
-            while (entries.hasNext()) {
-                final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
-                final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
-                if (wc == null || !wc.isAttached()) {
-                    Slog.e(TAG, "Attempt to operate on detached container: " + wc);
-                    continue;
-                }
-                final Task task = wc.asTask();
-                final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds();
-                if (task == null || !task.isAttached() || surfaceBounds == null) {
-                    continue;
-                }
-                if (!task.isOrganized()) {
-                    final Task parent = task.getParent() != null ? task.getParent().asTask() : null;
-                    // Also allow direct children of created-by-organizer tasks to be
-                    // controlled. In the future, these will become organized anyways.
-                    if (parent == null || !parent.mCreatedByOrganizer) {
-                        throw new IllegalArgumentException(
-                                "Can't manipulate non-organized task surface " + task);
-                    }
-                }
-                final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();
-                final SurfaceControl sc = task.getSurfaceControl();
-                sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top);
-                if (surfaceBounds.isEmpty()) {
-                    sft.setWindowCrop(sc, null);
-                } else {
-                    sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height());
-                }
-                task.setMainWindowSizeChangeTransaction(sft);
-            }
             if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
                 mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
                 mService.mTaskSupervisor.endDeferResume();
@@ -1899,7 +1865,7 @@
         if (keyguardState != null) {
             boolean keyguardShowing = keyguardState.getKeyguardShowing();
             boolean aodShowing = keyguardState.getAodShowing();
-            mService.setLockScreenShown(keyguardShowing, aodShowing);
+            mService.setLockScreenShownLocked(keyguardShowing, aodShowing);
         }
         return effects;
     }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e1f3f0e..b37bcc7 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -94,8 +94,6 @@
 
 namespace android {
 
-static const bool ENABLE_INPUT_FILTER_RUST = input_flags::enable_input_filter_rust_impl();
-
 // The exponent used to calculate the pointer speed scaling factor.
 // The scaling factor is calculated as 2 ^ (speed * exponent),
 // where the speed ranges from -7 to + 7 and is supplied by the user.
@@ -604,12 +602,14 @@
             return std::to_string(displayId.val());
         };
         dump += StringPrintf(INDENT "Display not interactive: %s\n",
-                             dumpSet(mLocked.nonInteractiveDisplays, streamableToString).c_str());
+                             dumpContainer(mLocked.nonInteractiveDisplays, streamableToString)
+                                     .c_str());
         dump += StringPrintf(INDENT "System UI Lights Out: %s\n",
                              toString(mLocked.systemUiLightsOut));
         dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
         dump += StringPrintf(INDENT "Display with Mouse Scaling Disabled: %s\n",
-                             dumpSet(mLocked.displaysWithMouseScalingDisabled, streamableToString)
+                             dumpContainer(mLocked.displaysWithMouseScalingDisabled,
+                                           streamableToString)
                                      .c_str());
         dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
                              toString(mLocked.pointerGesturesEnabled));
@@ -3248,27 +3248,21 @@
 static void nativeSetAccessibilityBounceKeysThreshold(JNIEnv* env, jobject nativeImplObj,
                                                       jint thresholdTimeMs) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
-    if (ENABLE_INPUT_FILTER_RUST) {
-        im->getInputManager()->getInputFilter().setAccessibilityBounceKeysThreshold(
-                static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
-    }
+    im->getInputManager()->getInputFilter().setAccessibilityBounceKeysThreshold(
+            static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
 }
 
 static void nativeSetAccessibilitySlowKeysThreshold(JNIEnv* env, jobject nativeImplObj,
                                                     jint thresholdTimeMs) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
-    if (ENABLE_INPUT_FILTER_RUST) {
-        im->getInputManager()->getInputFilter().setAccessibilitySlowKeysThreshold(
-                static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
-    }
+    im->getInputManager()->getInputFilter().setAccessibilitySlowKeysThreshold(
+            static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
 }
 
 static void nativeSetAccessibilityStickyKeysEnabled(JNIEnv* env, jobject nativeImplObj,
                                                     jboolean enabled) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
-    if (ENABLE_INPUT_FILTER_RUST) {
-        im->getInputManager()->getInputFilter().setAccessibilityStickyKeysEnabled(enabled);
-    }
+    im->getInputManager()->getInputFilter().setAccessibilityStickyKeysEnabled(enabled);
 }
 
 static void nativeSetInputMethodConnectionIsActive(JNIEnv* env, jobject nativeImplObj,
diff --git a/services/core/jni/com_android_server_utils_AnrTimer.cpp b/services/core/jni/com_android_server_utils_AnrTimer.cpp
index 2add5b0..24909ac 100644
--- a/services/core/jni/com_android_server_utils_AnrTimer.cpp
+++ b/services/core/jni/com_android_server_utils_AnrTimer.cpp
@@ -101,6 +101,9 @@
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
+// The current process.  This is cached here on startup.
+const pid_t sThisProcess = getpid();
+
 // Return true if the process exists and false if we cannot know.
 bool processExists(pid_t pid) {
     char path[PATH_MAX];
@@ -726,7 +729,7 @@
             uid(uid),
             timeout(timeout),
             extend(extend),
-            freeze(pid != 0 && freeze),
+            freeze(freeze),
             split(trace.earlyTimeout),
             action(trace.action),
             status(Running),
@@ -1188,8 +1191,11 @@
 }
 
 AnrTimerService::timer_id_t AnrTimerService::start(int pid, int uid, nsecs_t timeout) {
+    // Use the freezer only if the pid is not 0 (a nonsense value) and the pid is not self.
+    // Freezing the current process is a fatal error.
+    bool useFreezer = freeze_ && (pid != 0) && (pid != sThisProcess);
     AutoMutex _l(lock_);
-    Timer t(pid, uid, timeout, extend_, freeze_, tracer_.getConfig(pid));
+    Timer t(pid, uid, timeout, extend_, useFreezer, tracer_.getConfig(pid));
     insertLocked(t);
     t.start();
     counters_.started++;
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 2e126f1..014f0a2 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -204,7 +204,7 @@
     @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
     // this.mLock
     protected void handlePackageRemovedMultiModeLocked(String packageName, int userId) {
-        updateProvidersWhenPackageRemoved(new SettingsWrapper(mContext), packageName);
+        updateProvidersWhenPackageRemoved(new SettingsWrapper(mContext), packageName, userId);
 
         List<CredentialManagerServiceImpl> services = peekServiceListForUserLocked(userId);
         if (services == null) {
@@ -234,6 +234,47 @@
     }
 
     @GuardedBy("mLock")
+    @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
+    // this.mLock
+    protected void handleServiceRemovedMultiModeLocked(ComponentName componentName, int userId) {
+        updateProvidersWhenServiceRemoved(new SettingsWrapper(mContext), componentName, userId);
+
+        List<CredentialManagerServiceImpl> services = peekServiceListForUserLocked(userId);
+        if (services == null) {
+            return;
+        }
+
+        List<CredentialManagerServiceImpl> servicesToBeRemoved = new ArrayList<>();
+        for (CredentialManagerServiceImpl service : services) {
+            if (service != null) {
+                CredentialProviderInfo credentialProviderInfo = service.getCredentialProviderInfo();
+                ComponentName serviceComponentName =
+                        credentialProviderInfo.getServiceInfo().getComponentName();
+                if (serviceComponentName != null && serviceComponentName.equals(componentName)) {
+                    servicesToBeRemoved.add(service);
+                }
+            }
+        }
+
+        removeServicesLocked(servicesToBeRemoved, userId);
+    }
+
+    @GuardedBy("mLock")
+    @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
+    // this.mLock
+    private void removeServicesLocked(
+            List<CredentialManagerServiceImpl> servicesToBeRemoved, int userId) {
+        // Iterate over all the services to be removed, and remove them from the user configurable
+        // services cache, the system services cache as well as the setting key-value pair.
+        for (CredentialManagerServiceImpl serviceToBeRemoved : servicesToBeRemoved) {
+            removeServiceFromCache(serviceToBeRemoved, userId);
+            removeServiceFromSystemServicesCache(serviceToBeRemoved, userId);
+            CredentialDescriptionRegistry.forUser(userId)
+                    .evictProviderWithPackageName(serviceToBeRemoved.getServicePackageName());
+        }
+    }
+
+    @GuardedBy("mLock")
     private void removeServiceFromSystemServicesCache(
             CredentialManagerServiceImpl serviceToBeRemoved, int userId) {
         if (mSystemServicesCacheList.get(userId) != null) {
@@ -1136,76 +1177,211 @@
         }
     }
 
-    /** Updates the list of providers when an app is uninstalled. */
-    public static void updateProvidersWhenPackageRemoved(
-            SettingsWrapper settingsWrapper, String packageName) {
-        Slog.i(TAG, "updateProvidersWhenPackageRemoved");
+    /** Updates the list of providers when a particular service within an app is to be removed. */
+    public static void updateProvidersWhenServiceRemoved(
+            SettingsWrapper settingsWrapper, ComponentName componentName, int userId) {
+        Slog.i(TAG, "updateProvidersWhenServiceRemoved for: "
+                + componentName.flattenToString());
 
-        // Get the current providers.
-        String rawProviders =
+        // Get the current primary providers.
+        String rawPrimaryProviders =
                 settingsWrapper.getStringForUser(
-                        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, UserHandle.myUserId());
-        if (rawProviders == null) {
-            Slog.w(TAG, "settings key is null");
-            return;
+                        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, userId);
+        if (TextUtils.isEmpty(rawPrimaryProviders)) {
+            Slog.w(TAG, "primary settings key is null");
+        } else {
+            // Remove any service from the primary setting that matches with the service
+            // being removed, and set the filtered list back to the settings key.
+            Set<String> filteredPrimaryProviders = getStoredProvidersExceptService(
+                    rawPrimaryProviders, componentName);
+
+            // If there are no more primary providers AND there is no autofill provider either,
+            // that means all providers must be cleared as that is what the Settings UI app
+            // displays to the user.If the autofill provider is present then UI shows that as the
+            // preferred service and other credential provider services can continue to work.
+            if (filteredPrimaryProviders.isEmpty()
+                    && !isAutofillProviderPresent(settingsWrapper, userId)) {
+                Slog.d(TAG, "Clearing all credential providers");
+                if (clearAllCredentialProviders(settingsWrapper, userId)) {
+                    return;
+                }
+                Slog.e(TAG, "Failed to clear all credential providers");
+            }
+
+            // Set the filtered primary providers to the settings key
+            if (!settingsWrapper.putStringForUser(
+                    Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
+                    String.join(":", filteredPrimaryProviders),
+                    UserHandle.myUserId(),
+                    /* overrideableByRestore= */ true)) {
+                Slog.e(TAG, "Failed to remove primary service: " + componentName);
+                return;
+            }
         }
 
-        // Remove any providers from the primary setting that contain the package name
-        // being removed.
-        Set<String> primaryProviders = getStoredProviders(rawProviders, packageName);
+        // Read the credential providers to remove any reference of the removed service.
+        String rawCredentialProviders =
+                settingsWrapper.getStringForUser(
+                        Settings.Secure.CREDENTIAL_SERVICE, UserHandle.myUserId());
+
+        // Remove any provider services that are same as the one being removed.
+        Set<String> filteredCredentialProviders = getStoredProvidersExceptService(
+                rawCredentialProviders, componentName);
         if (!settingsWrapper.putStringForUser(
-                Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
-                String.join(":", primaryProviders),
+                Settings.Secure.CREDENTIAL_SERVICE,
+                String.join(":", filteredCredentialProviders),
                 UserHandle.myUserId(),
                 /* overrideableByRestore= */ true)) {
-            Slog.e(TAG, "Failed to remove primary package: " + packageName);
-            return;
+            Slog.e(TAG, "Failed to remove secondary service: " + componentName);
         }
+    }
 
+    private static boolean isAutofillProviderPresent(SettingsWrapper settingsWrapper, int userId) {
         // Read the autofill provider so we don't accidentally erase it.
         String autofillProvider =
                 settingsWrapper.getStringForUser(
-                        Settings.Secure.AUTOFILL_SERVICE, UserHandle.myUserId());
+                        Settings.Secure.AUTOFILL_SERVICE, userId);
+
+        return autofillProvider != null && !autofillProvider.isEmpty()
+                && !isPlaceholderAutofillProvider(autofillProvider, settingsWrapper);
+    }
+
+    private static boolean isPlaceholderAutofillProvider(String autofillProvider,
+            SettingsWrapper settingsWrapper) {
 
         // If there is an autofill provider and it is the credential autofill service indicating
         // that the currently selected primary provider does not support autofill
         // then we should keep as is
         String credentialAutofillService = settingsWrapper.mContext.getResources().getString(
                 R.string.config_defaultCredentialManagerAutofillService);
-        if (autofillProvider != null && primaryProviders.isEmpty() && !TextUtils.equals(
-                autofillProvider, credentialAutofillService)) {
-            // If the existing autofill provider is from the app being removed
-            // then erase the autofill service setting.
-            ComponentName cn = ComponentName.unflattenFromString(autofillProvider);
-            if (cn != null && cn.getPackageName().equals(packageName)) {
-                if (!settingsWrapper.putStringForUser(
-                        Settings.Secure.AUTOFILL_SERVICE,
-                        "",
-                        UserHandle.myUserId(),
-                        /* overrideableByRestore= */ true)) {
-                    Slog.e(TAG, "Failed to remove autofill package: " + packageName);
+        return autofillProvider != null && TextUtils.equals(
+                autofillProvider, credentialAutofillService);
+    }
+
+    private static boolean clearAllCredentialProviders(SettingsWrapper settingsWrapper,
+            int userId) {
+        if (!settingsWrapper.putStringForUser(
+                Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
+                null,
+                userId,
+                /* overrideableByRestore= */ true)) {
+            return false;
+        }
+        return settingsWrapper.putStringForUser(
+                Settings.Secure.CREDENTIAL_SERVICE,
+                null,
+                userId,
+                /* overrideableByRestore= */ true);
+    }
+
+    /** Updates the list of providers when an app is uninstalled. */
+    public static void updateProvidersWhenPackageRemoved(
+            SettingsWrapper settingsWrapper, String packageName, int userId) {
+        Slog.i(TAG, "updateProvidersWhenPackageRemoved");
+
+        // Get the current providers.
+        String rawProviders =
+                settingsWrapper.getStringForUser(
+                        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, userId);
+        if (rawProviders == null) {
+            Slog.w(TAG, "settings key is null");
+        } else {
+            // Remove any providers from the primary setting that contain the package name
+            // being removed.
+            Set<String> primaryProviders = getStoredProvidersExceptPackage(rawProviders,
+                    packageName);
+            if (!settingsWrapper.putStringForUser(
+                    Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
+                    String.join(":", primaryProviders),
+                    userId,
+                    /* overrideableByRestore= */ true)) {
+                Slog.e(TAG, "Failed to remove primary package: " + packageName);
+                return;
+            }
+
+            // Read the autofill provider so we don't accidentally erase it.
+            String autofillProvider =
+                    settingsWrapper.getStringForUser(
+                            Settings.Secure.AUTOFILL_SERVICE, userId);
+
+            // If there is an autofill provider and it is the credential autofill service indicating
+            // that the currently selected primary provider does not support autofill
+            // then we should keep as is
+            String credentialAutofillService = settingsWrapper.mContext.getResources().getString(
+                    R.string.config_defaultCredentialManagerAutofillService);
+            if (autofillProvider != null && primaryProviders.isEmpty() && !TextUtils.equals(
+                    autofillProvider, credentialAutofillService)) {
+                // If the existing autofill provider is from the app being removed
+                // then erase the autofill service setting.
+                ComponentName cn = ComponentName.unflattenFromString(autofillProvider);
+                if (cn != null && cn.getPackageName().equals(packageName)) {
+                    if (!settingsWrapper.putStringForUser(
+                            Settings.Secure.AUTOFILL_SERVICE,
+                            "",
+                            userId,
+                            /* overrideableByRestore= */ true)) {
+                        Slog.e(TAG, "Failed to remove autofill package: " + packageName);
+                    }
                 }
             }
+
+            // If there are no more primary providers AND there is no autofill provider either,
+            // that means all providers must be cleared as that is what the Settings UI app
+            // displays to the user.If the autofill provider is present then UI shows that as the
+            // preferred service and other credential provider services can continue to work.
+            if (primaryProviders.isEmpty() && !isAutofillProviderPresent(settingsWrapper, userId)) {
+                Slog.d(TAG, "Clearing all credential providers");
+                if (clearAllCredentialProviders(settingsWrapper, userId)) {
+                    return;
+                }
+                Slog.e(TAG, "Failed to clear all credential providers");
+            }
         }
 
         // Read the credential providers to remove any reference of the removed app.
         String rawCredentialProviders =
                 settingsWrapper.getStringForUser(
-                        Settings.Secure.CREDENTIAL_SERVICE, UserHandle.myUserId());
+                        Settings.Secure.CREDENTIAL_SERVICE, userId);
 
         // Remove any providers that belong to the removed app.
-        Set<String> credentialProviders = getStoredProviders(rawCredentialProviders, packageName);
+        Set<String> credentialProviders = getStoredProvidersExceptPackage(
+                rawCredentialProviders, packageName);
         if (!settingsWrapper.putStringForUser(
                 Settings.Secure.CREDENTIAL_SERVICE,
                 String.join(":", credentialProviders),
-                UserHandle.myUserId(),
+                userId,
                 /* overrideableByRestore= */ true)) {
             Slog.e(TAG, "Failed to remove secondary package: " + packageName);
         }
     }
 
     /** Gets the list of stored providers from a string removing any mention of package name. */
-    public static Set<String> getStoredProviders(String rawProviders, String packageName) {
+    public static Set<String> getStoredProvidersExceptService(String rawProviders,
+            ComponentName componentName) {
+        // If the app being removed matches any of the package names from
+        // this list then don't add it in the output.
+        Set<String> providers = new HashSet<>();
+        if (rawProviders == null || componentName == null) {
+            return providers;
+        }
+        for (String rawComponentName : rawProviders.split(":")) {
+            if (TextUtils.isEmpty(rawComponentName) || rawComponentName.equals("null")) {
+                Slog.d(TAG, "provider component name is empty or null");
+                continue;
+            }
+
+            ComponentName cn = ComponentName.unflattenFromString(rawComponentName);
+            if (cn != null && !cn.equals(componentName)) {
+                providers.add(cn.flattenToString());
+            }
+        }
+
+        return providers;
+    }
+
+    /** Gets the list of stored providers from a string removing any mention of package name. */
+    public static Set<String> getStoredProvidersExceptPackage(
+            String rawProviders, String packageName) {
         // If the app being removed matches any of the package names from
         // this list then don't add it in the output.
         Set<String> providers = new HashSet<>();
@@ -1213,8 +1389,7 @@
             return providers;
         }
         for (String rawComponentName : rawProviders.split(":")) {
-            if (TextUtils.isEmpty(rawComponentName)
-                    || rawComponentName.equals("null")) {
+            if (TextUtils.isEmpty(rawComponentName) || rawComponentName.equals("null")) {
                 Slog.d(TAG, "provider component name is empty or null");
                 continue;
             }
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
index b86db06..40554ac 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
@@ -57,6 +57,13 @@
         }
     }
 
+    @Nullable
+    @Override
+    @GuardedBy("mLock")
+    public ComponentName getServiceComponentName() {
+        return getComponentName();
+    }
+
     @GuardedBy("mLock")
     public ComponentName getComponentName() {
         return mInfo.getServiceInfo().getComponentName();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 25e9f8a..c974d9e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1799,7 +1799,7 @@
             mSystemServiceManager.startService(LogcatManagerService.class);
             t.traceEnd();
 
-            if (!isWatch && !isTv && !isAutomotive
+            if (!isWatch && !isTv && !isAutomotive && !isDesktop
                     && android.security.Flags.aflApi()) {
                 t.traceBegin("StartIntrusionDetectionService");
                 mSystemServiceManager.startService(IntrusionDetectionService.class);
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 073ee31..ea85710 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -23,6 +23,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
@@ -51,7 +52,6 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.pm.UserManagerInternal;
 
 import java.io.FileDescriptor;
@@ -62,26 +62,28 @@
 public class SupervisionService extends ISupervisionManager.Stub {
     private static final String LOG_TAG = "SupervisionService";
 
-    private final Context mContext;
-
     // TODO(b/362756788): Does this need to be a LockGuard lock?
     private final Object mLockDoNoUseDirectly = new Object();
 
     @GuardedBy("getLockObject()")
     private final SparseArray<SupervisionUserData> mUserData = new SparseArray<>();
 
-    private final DevicePolicyManagerInternal mDpmInternal;
-    private final PackageManager mPackageManager;
-    private final UserManagerInternal mUserManagerInternal;
+    private final Context mContext;
+    private final Injector mInjector;
+    final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl();
 
     public SupervisionService(Context context) {
         mContext = context.createAttributionContext(LOG_TAG);
-        mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
-        mPackageManager = context.getPackageManager();
-        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
-        mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
+        mInjector = new Injector(context);
+        mInjector.getUserManagerInternal().addUserLifecycleListener(new UserLifecycleListener());
     }
 
+    /**
+     * Returns whether supervision is enabled for the given user.
+     *
+     * <p>Supervision is automatically enabled when the supervision app becomes the profile owner or
+     * explicitly enabled via an internal call to {@link #setSupervisionEnabledForUser}.
+     */
     @Override
     public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
         if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
@@ -93,6 +95,28 @@
     }
 
     @Override
+    public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
+        if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
+            enforcePermission(INTERACT_ACROSS_USERS);
+        }
+        setSupervisionEnabledForUserInternal(userId, enabled, getSystemSupervisionPackage());
+    }
+
+    /**
+     * Returns the package name of the active supervision app or null if supervision is disabled.
+     */
+    @Override
+    @Nullable
+    public String getActiveSupervisionAppPackage(@UserIdInt int userId) {
+        if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
+            enforcePermission(INTERACT_ACROSS_USERS);
+        }
+        synchronized (getLockObject()) {
+            return getUserDataLocked(userId).supervisionAppPackage;
+        }
+    }
+
+    @Override
     public void onShellCommand(
             @Nullable FileDescriptor in,
             @Nullable FileDescriptor out,
@@ -114,7 +138,7 @@
             pw.println("SupervisionService state:");
             pw.increaseIndent();
 
-            List<UserInfo> users = mUserManagerInternal.getUsers(false);
+            List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(false);
             synchronized (getLockObject()) {
                 for (var user : users) {
                     getUserDataLocked(user.id).dump(pw);
@@ -140,35 +164,54 @@
         return data;
     }
 
-    void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
+    /**
+     * Sets supervision as enabled or disabled for the given user and, in case supervision is being
+     * enabled, the package of the active supervision app.
+     */
+    private void setSupervisionEnabledForUserInternal(
+            @UserIdInt int userId, boolean enabled, @Nullable String supervisionAppPackage) {
         synchronized (getLockObject()) {
-            getUserDataLocked(userId).supervisionEnabled = enabled;
+            SupervisionUserData data = getUserDataLocked(userId);
+            data.supervisionEnabled = enabled;
+            data.supervisionAppPackage = enabled ? supervisionAppPackage : null;
         }
     }
 
-    /** Ensures that supervision is enabled when supervision app is the profile owner. */
+    /** Ensures that supervision is enabled when the supervision app is the profile owner. */
     private void syncStateWithDevicePolicyManager(@UserIdInt int userId) {
-        if (isProfileOwner(userId)) {
-            setSupervisionEnabledForUser(userId, true);
+        final DevicePolicyManagerInternal dpmInternal = mInjector.getDpmInternal();
+        final ComponentName po =
+                dpmInternal != null ? dpmInternal.getProfileOwnerAsUser(userId) : null;
+
+        if (po != null && po.getPackageName().equals(getSystemSupervisionPackage())) {
+            setSupervisionEnabledForUserInternal(userId, true, po.getPackageName());
+        } else if (po != null && po.equals(getSupervisionProfileOwnerComponent())) {
+            // TODO(b/392071637): Consider not enabling supervision in case profile owner is given
+            // to the legacy supervision profile owner component.
+            setSupervisionEnabledForUserInternal(userId, true, po.getPackageName());
         } else {
             // TODO(b/381428475): Avoid disabling supervision when the app is not the profile owner.
             // This might only be possible after introducing specific and public APIs to enable
-            // supervision.
-            setSupervisionEnabledForUser(userId, false);
+            // and disable supervision.
+            setSupervisionEnabledForUserInternal(userId, false, /* supervisionAppPackage= */ null);
         }
     }
 
-    /** Returns whether the supervision app has profile owner status. */
-    private boolean isProfileOwner(@UserIdInt int userId) {
-        ComponentName profileOwner =
-                mDpmInternal != null ? mDpmInternal.getProfileOwnerAsUser(userId) : null;
-        return profileOwner != null && isSupervisionAppPackage(profileOwner.getPackageName());
+    /**
+     * Returns the {@link ComponentName} of the supervision profile owner component.
+     *
+     * <p>This component is used to give GMS Kids Module permission to supervise the device and may
+     * still be active during the transition to the {@code SYSTEM_SUPERVISION} role.
+     */
+    private ComponentName getSupervisionProfileOwnerComponent() {
+        return ComponentName.unflattenFromString(
+                mContext.getResources()
+                        .getString(R.string.config_defaultSupervisionProfileOwnerComponent));
     }
 
-    /** Returns whether the given package name belongs to the supervision role holder. */
-    private boolean isSupervisionAppPackage(String packageName) {
-        return packageName.equals(
-                mContext.getResources().getString(R.string.config_systemSupervision));
+    /** Returns the package assigned to the {@code SYSTEM_SUPERVISION} role. */
+    private String getSystemSupervisionPackage() {
+        return mContext.getResources().getString(R.string.config_systemSupervision);
     }
 
     /** Enforces that the caller has the given permission. */
@@ -177,6 +220,41 @@
                 mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED);
     }
 
+    /** Provides local services in a lazy manner. */
+    static class Injector {
+        private final Context mContext;
+        private DevicePolicyManagerInternal mDpmInternal;
+        private PackageManager mPackageManager;
+        private UserManagerInternal mUserManagerInternal;
+
+        Injector(Context context) {
+            mContext = context;
+        }
+
+        @Nullable
+        DevicePolicyManagerInternal getDpmInternal() {
+            if (mDpmInternal == null) {
+                mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
+            }
+            return mDpmInternal;
+        }
+
+        PackageManager getPackageManager() {
+            if (mPackageManager == null) {
+                mPackageManager = mContext.getPackageManager();
+            }
+            return mPackageManager;
+        }
+
+        UserManagerInternal getUserManagerInternal() {
+            if (mUserManagerInternal == null) {
+                mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+            }
+            return mUserManagerInternal;
+        }
+    }
+
+    /** Publishes local and binder services and allows the service to act during initialization. */
     public static class Lifecycle extends SystemService {
         private final SupervisionService mSupervisionService;
 
@@ -201,6 +279,7 @@
         }
 
         @VisibleForTesting
+        @SuppressLint("MissingPermission") // not needed for a service
         void registerProfileOwnerListener() {
             IntentFilter poIntentFilter = new IntentFilter();
             poIntentFilter.addAction(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED);
@@ -209,7 +288,7 @@
                     .registerReceiverForAllUsers(
                             new ProfileOwnerBroadcastReceiver(),
                             poIntentFilter,
-                            /* brodcastPermission= */ null,
+                            /* broadcastPermission= */ null,
                             /* scheduler= */ null);
         }
 
@@ -228,19 +307,22 @@
         }
     }
 
-    final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl();
-
+    /** Implementation of the local service, API used by other services. */
     private final class SupervisionManagerInternalImpl extends SupervisionManagerInternal {
         @Override
         public boolean isActiveSupervisionApp(int uid) {
-            String[] packages = mPackageManager.getPackagesForUid(uid);
-            if (packages == null) {
+            int userId = UserHandle.getUserId(uid);
+            String supervisionAppPackage = getActiveSupervisionAppPackage(userId);
+            if (supervisionAppPackage == null) {
                 return false;
             }
-            for (var packageName : packages) {
-                if (SupervisionService.this.isSupervisionAppPackage(packageName)) {
-                    int userId = UserHandle.getUserId(uid);
-                    return SupervisionService.this.isSupervisionEnabledForUser(userId);
+
+            String[] packages = mInjector.getPackageManager().getPackagesForUid(uid);
+            if (packages != null) {
+                for (var packageName : packages) {
+                    if (supervisionAppPackage.equals(packageName)) {
+                        return true;
+                    }
                 }
             }
             return false;
@@ -274,6 +356,7 @@
         }
     }
 
+    /** Deletes user data when the user gets removed. */
     private final class UserLifecycleListener implements UserManagerInternal.UserLifecycleListener {
         @Override
         public void onUserRemoved(UserInfo user) {
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
index 2adaae3..976642b 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
@@ -32,16 +32,18 @@
             return handleDefaultCommands(null);
         }
         switch (cmd) {
-            case "enable": return setEnabled(true);
-            case "disable": return setEnabled(false);
-            default: return handleDefaultCommands(cmd);
+            case "enable":
+                return setEnabled(true);
+            case "disable":
+                return setEnabled(false);
+            default:
+                return handleDefaultCommands(cmd);
         }
     }
 
     private int setEnabled(boolean enabled) {
-        final var pw = getOutPrintWriter();
         final var userId = UserHandle.parseUserArg(getNextArgRequired());
-        mService.setSupervisionEnabledForUser(userId, enabled);
+        mService.mInternal.setSupervisionEnabledForUser(userId, enabled);
         return 0;
     }
 
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
index 1dd48f5..06acb91 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
@@ -26,6 +26,7 @@
 public class SupervisionUserData {
     public final @UserIdInt int userId;
     public boolean supervisionEnabled;
+    @Nullable public String supervisionAppPackage;
     public boolean supervisionLockScreenEnabled;
     @Nullable public PersistableBundle supervisionLockScreenOptions;
 
@@ -38,6 +39,7 @@
         pw.println("User " + userId + ":");
         pw.increaseIndent();
         pw.println("supervisionEnabled: " + supervisionEnabled);
+        pw.println("supervisionAppPackage: " + supervisionAppPackage);
         pw.println("supervisionLockScreenEnabled: " + supervisionLockScreenEnabled);
         pw.println("supervisionLockScreenOptions: " + supervisionLockScreenOptions);
         pw.decreaseIndent();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
index 01061f1..0466e75 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
@@ -29,6 +29,7 @@
 import org.mockito.kotlin.whenever
 
 private val TEST_PLUGIN_TYPE = PluginType(Int::class.java, "test_type")
+private val DISPLAY_ID = "display_id"
 
 @SmallTest
 class PluginManagerTest {
@@ -62,18 +63,18 @@
     fun testSubscribe() {
         val pluginManager = createPluginManager()
 
-        pluginManager.subscribe(TEST_PLUGIN_TYPE, mockListener)
+        pluginManager.subscribe(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
 
-        verify(testInjector.mockStorage).addListener(TEST_PLUGIN_TYPE, mockListener)
+        verify(testInjector.mockStorage).addListener(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
     }
 
     @Test
     fun testUnsubscribe() {
         val pluginManager = createPluginManager()
 
-        pluginManager.unsubscribe(TEST_PLUGIN_TYPE, mockListener)
+        pluginManager.unsubscribe(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
 
-        verify(testInjector.mockStorage).removeListener(TEST_PLUGIN_TYPE, mockListener)
+        verify(testInjector.mockStorage).removeListener(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
     }
 
     private fun createPluginManager(enabled: Boolean = true): PluginManager {
@@ -86,11 +87,15 @@
         val mockPlugin1 = mock<Plugin>()
         val mockPlugin2 = mock<Plugin>()
 
-        override fun getPluginStorage(): PluginStorage {
+        override fun getPluginStorage(enabledTypes: Set<PluginType<*>>): PluginStorage {
             return mockStorage
         }
 
-        override fun loadPlugins(context: Context?, storage: PluginStorage?): List<Plugin> {
+        override fun loadPlugins(
+            context: Context?,
+            storage: PluginStorage?,
+            enabledTypes: Set<PluginType<*>>
+        ): List<Plugin> {
             return listOf(mockPlugin1, mockPlugin2)
         }
     }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
index 218e341..2b06126 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
@@ -23,19 +23,21 @@
 
 private val TEST_PLUGIN_TYPE1 = PluginType(String::class.java, "test_type1")
 private val TEST_PLUGIN_TYPE2 = PluginType(String::class.java, "test_type2")
+private val DISPLAY_ID_1 = "display_1"
+private val DISPLAY_ID_2 = "display_2"
 
 @SmallTest
 class PluginStorageTest {
 
-    val storage = PluginStorage()
+    var storage = PluginStorage(setOf(TEST_PLUGIN_TYPE1, TEST_PLUGIN_TYPE2))
 
     @Test
     fun testUpdateValue() {
         val type1Value = "value1"
         val testChangeListener = TestPluginChangeListener<String>()
-        storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
 
-        storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+        storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
 
         assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
     }
@@ -44,9 +46,9 @@
     fun testAddListener() {
         val type1Value = "value1"
         val testChangeListener = TestPluginChangeListener<String>()
-        storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+        storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
 
-        storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
 
         assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
     }
@@ -55,10 +57,10 @@
     fun testRemoveListener() {
         val type1Value = "value1"
         val testChangeListener = TestPluginChangeListener<String>()
-        storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
-        storage.removeListener(TEST_PLUGIN_TYPE1, testChangeListener)
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
+        storage.removeListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
 
-        storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+        storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
 
         assertThat(testChangeListener.receivedValue).isNull()
     }
@@ -68,10 +70,10 @@
         val type1Value = "value1"
         val type2Value = "value2"
         val testChangeListener = TestPluginChangeListener<String>()
-        storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
-        storage.updateValue(TEST_PLUGIN_TYPE2, type2Value)
+        storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+        storage.updateValue(TEST_PLUGIN_TYPE2, DISPLAY_ID_1, type2Value)
 
-        storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
 
         assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
     }
@@ -81,15 +83,74 @@
         val type1Value = "value1"
         val testChangeListener1 = TestPluginChangeListener<String>()
         val testChangeListener2 = TestPluginChangeListener<String>()
-        storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener1)
-        storage.addListener(TEST_PLUGIN_TYPE2, testChangeListener2)
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+        storage.addListener(TEST_PLUGIN_TYPE2, DISPLAY_ID_1, testChangeListener2)
 
-        storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+        storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
 
         assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
         assertThat(testChangeListener2.receivedValue).isNull()
     }
 
+    @Test
+    fun testDisabledPluginType() {
+        storage = PluginStorage(setOf(TEST_PLUGIN_TYPE2))
+        val type1Value = "value1"
+        val testChangeListener = TestPluginChangeListener<String>()
+        storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
+
+        assertThat(testChangeListener.receivedValue).isNull()
+    }
+
+    @Test
+    fun testUpdateGlobal_noDisplaySpecificValue() {
+        val type1Value = "value1"
+        val testChangeListener1 = TestPluginChangeListener<String>()
+        val testChangeListener2 = TestPluginChangeListener<String>()
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+        storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1Value)
+
+        assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
+        assertThat(testChangeListener2.receivedValue).isEqualTo(type1Value)
+    }
+
+    @Test
+    fun testUpdateGlobal_withDisplaySpecificValue() {
+        val type1Value = "value1"
+        val type1GlobalValue = "value1Global"
+        val testChangeListener1 = TestPluginChangeListener<String>()
+        val testChangeListener2 = TestPluginChangeListener<String>()
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+        storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+        storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1GlobalValue)
+
+        assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
+        assertThat(testChangeListener2.receivedValue).isEqualTo(type1GlobalValue)
+    }
+
+    @Test
+    fun testUpdateGlobal_withDisplaySpecificValueRemoved() {
+        val type1Value = "value1"
+        val type1GlobalValue = "value1Global"
+        val testChangeListener1 = TestPluginChangeListener<String>()
+        val testChangeListener2 = TestPluginChangeListener<String>()
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+        storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+        storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+        storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1GlobalValue)
+        storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, null)
+
+        assertThat(testChangeListener1.receivedValue).isEqualTo(type1GlobalValue)
+        assertThat(testChangeListener2.receivedValue).isEqualTo(type1GlobalValue)
+    }
+
     private class TestPluginChangeListener<T> : PluginChangeListener<T> {
         var receivedValue: T? = null
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 8dc8c14..cb52f18 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -151,6 +151,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.flag.util.FlagSetException;
@@ -192,7 +193,9 @@
 import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatchers;
+import org.mockito.InOrder;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.quality.Strictness;
 import org.mockito.stubbing.Answer;
 
@@ -435,6 +438,7 @@
     private void disableFlagsNotSetByAnnotation() {
         try {
             mSetFlagsRule.disableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS);
+            mSetFlagsRule.disableFlags(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND);
         } catch (FlagSetException fse) {
             // Expected if the test about to be run requires this enabled.
         }
@@ -948,6 +952,28 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND)
+    public void testWakelockOrdering() throws Exception {
+        final long triggerTime = mNowElapsedTest + 5000;
+        final PendingIntent alarmPi = getNewMockPendingIntent();
+        setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
+
+        mNowElapsedTest = mTestTimer.getElapsed();
+        mTestTimer.expire();
+
+        final InOrder inOrder = Mockito.inOrder(alarmPi, mWakeLock);
+        inOrder.verify(mWakeLock).acquire();
+
+        final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
+                ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
+        inOrder.verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
+                onFinishedCaptor.capture(), any(Handler.class), isNull(), any());
+        onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null);
+
+        inOrder.verify(mWakeLock).release();
+    }
+
+    @Test
     public void testMinFuturityCoreUid() {
         setDeviceConfigLong(KEY_MIN_FUTURITY, 10L);
         assertEquals(10, mService.mConstants.MIN_FUTURITY);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index fe7cc92..1ef758c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -3395,6 +3395,51 @@
         }
     }
 
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoAll_BindUiServiceFromClientService() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+        ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+        ServiceRecord s = makeServiceRecord(client);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, SystemClock.uptimeMillis());
+        bindService(app, client, null, null, 0, mock(IBinder.class));
+
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        updateOomAdj(app, client);
+        if (Flags.raiseBoundUiServiceThreshold()) {
+            assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+                    "service");
+        } else {
+            final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                    ? sFirstUiCachedAdj : sFirstCachedAdj;
+            assertProcStates(app, PROCESS_STATE_SERVICE, expectedAdj, SCHED_GROUP_BACKGROUND,
+                    "cch-bound-ui-services");
+        }
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoAll_BindUiServiceFromClientHome() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+        ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+
+        WindowProcessController wpc = client.getWindowProcessController();
+        doReturn(true).when(wpc).isHomeProcess();
+        bindService(app, client, null, null, 0, mock(IBinder.class));
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        updateOomAdj(app, client);
+
+        final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                ? sFirstUiCachedAdj : sFirstCachedAdj;
+        assertProcStates(app, PROCESS_STATE_HOME, expectedAdj, SCHED_GROUP_BACKGROUND,
+                "cch-bound-ui-services");
+    }
+
     private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName,
             String packageName, boolean hasShownUi) {
         return new ProcessRecordBuilder(pid, uid, processName, packageName).setHasShownUi(
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
index 27eada0..d6349fc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -18,6 +18,7 @@
 
 import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE;
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
 import static android.os.Process.INVALID_UID;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -31,9 +32,10 @@
 import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_USER_STOPPED;
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
 import static com.android.server.am.PendingIntentRecord.cancelReasonToString;
+import static com.android.window.flags.Flags.balClearAllowlistDuration;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -207,10 +209,17 @@
         PendingIntentRecord.TempAllowListDuration allowlistDurationLocked =
                 pir.getAllowlistDurationLocked(token);
         assertEquals(1000, allowlistDurationLocked.duration);
+        assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                allowlistDurationLocked.type);
         pir.clearAllowBgActivityStarts(token);
         PendingIntentRecord.TempAllowListDuration allowlistDurationLockedAfterClear =
                 pir.getAllowlistDurationLocked(token);
-        assertNull(allowlistDurationLockedAfterClear);
+        assertNotNull(allowlistDurationLockedAfterClear);
+        assertEquals(1000, allowlistDurationLockedAfterClear.duration);
+        assertEquals(balClearAllowlistDuration()
+                        ? TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED
+                        : TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                allowlistDurationLocked.type);
     }
 
     private void assertCancelReason(int expectedReason, int actualReason) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 1cf6556..d80fd20 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -144,6 +144,8 @@
     private static ComponentName sImageWallpaperComponentName;
     private static ComponentName sDefaultWallpaperComponent;
 
+    private static ComponentName sFallbackWallpaperComponentName;
+
     private IPackageManager mIpm = AppGlobals.getPackageManager();
 
     @Mock
@@ -195,6 +197,8 @@
                 sContext.getResources().getString(R.string.image_wallpaper_component));
         // Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper.
         sDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(sContext);
+        sFallbackWallpaperComponentName = ComponentName.unflattenFromString(
+                sContext.getResources().getString(R.string.fallback_wallpaper_component));
 
         if (sDefaultWallpaperComponent == null) {
             sDefaultWallpaperComponent = sImageWallpaperComponentName;
@@ -205,6 +209,9 @@
         }
 
         sContext.addMockService(sImageWallpaperComponentName, sWallpaperService);
+        if (sFallbackWallpaperComponentName != null) {
+            sContext.addMockService(sFallbackWallpaperComponentName, sWallpaperService);
+        }
     }
 
     @AfterClass
@@ -216,6 +223,7 @@
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
         sImageWallpaperComponentName = null;
         sDefaultWallpaperComponent = null;
+        sFallbackWallpaperComponentName = null;
         reset(sContext);
     }
 
@@ -306,6 +314,7 @@
      * Tests that internal basic data should be correct after boot up.
      */
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
     public void testDataCorrectAfterBoot() {
         mService.switchUser(USER_SYSTEM, null);
 
@@ -719,10 +728,10 @@
         final int testDisplayId = 2;
         setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
 
-        // WHEN displayId, 2, is ready.
+        // WHEN display ID, 2, is ready.
         WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
                 WallpaperManagerInternal.class);
-        wallpaperManagerInternal.onDisplayReady(testDisplayId);
+        wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
 
         // Then there is a connection established for the system & lock wallpaper for display ID, 2.
         verify(mockIWallpaperService).attach(
@@ -759,10 +768,10 @@
         final int testDisplayId = 2;
         setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
 
-        // WHEN displayId, 2, is ready.
+        // WHEN display ID, 2, is ready.
         WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
                 WallpaperManagerInternal.class);
-        wallpaperManagerInternal.onDisplayReady(testDisplayId);
+        wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
 
         // Then there is a connection established for the system wallpaper for display ID, 2.
         verify(mockIWallpaperService).attach(
@@ -791,6 +800,40 @@
                 /* info= */ any(),
                 /* description= */ any());
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+    public void displayAdded_wallpaperIncompatibleForDisplay_shouldAttachFallbackWallpaperService()
+            throws Exception {
+        final int testUserId = USER_SYSTEM;
+        mService.switchUser(testUserId, null);
+        IWallpaperService mockIWallpaperService = mock(IWallpaperService.class);
+        mService.mFallbackWallpaper.connection.mService = mockIWallpaperService;
+        // GIVEN there are two displays: DEFAULT_DISPLAY, 2
+        final int testDisplayId = 2;
+        setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+        // GIVEN the wallpaper isn't compatible with display ID, 2
+        mService.removeWallpaperCompatibleDisplayForTest(testDisplayId);
+
+        // WHEN display ID, 2, is ready.
+        WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
+                WallpaperManagerInternal.class);
+        wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
+
+        // Then there is a connection established for the fallback wallpaper for display ID, 2.
+        verify(mockIWallpaperService).attach(
+                /* connection= */ eq(mService.mFallbackWallpaper.connection),
+                /* windowToken= */ any(),
+                /* windowType= */ anyInt(),
+                /* isPreview= */ anyBoolean(),
+                /* reqWidth= */ anyInt(),
+                /* reqHeight= */ anyInt(),
+                /* padding= */ any(),
+                /* displayId= */ eq(testDisplayId),
+                /* which= */ eq(FLAG_SYSTEM | FLAG_LOCK),
+                /* info= */ any(),
+                /* description= */ any());
+    }
     // Verify a secondary display added end
 
     // Verify a secondary display removed started
@@ -810,19 +853,19 @@
         // GIVEN there are two displays: DEFAULT_DISPLAY, 2
         final int testDisplayId = 2;
         setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
-        // GIVEN wallpaper connections have been established for displayID, 2.
+        // GIVEN wallpaper connections have been established for display ID, 2.
         WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
                 WallpaperManagerInternal.class);
-        wallpaperManagerInternal.onDisplayReady(testDisplayId);
+        wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
         // Save displayConnector for displayId 2 before display removal.
         WallpaperManagerService.DisplayConnector displayConnector =
                 wallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
 
-        // WHEN displayId, 2, is removed.
+        // WHEN display ID, 2, is removed.
         DisplayListener displayListener = displayListenerCaptor.getValue();
         displayListener.onDisplayRemoved(testDisplayId);
 
-        // Then the wallpaper connection for displayId, 2, is detached.
+        // Then the wallpaper connection for display ID, 2, is detached.
         verify(mockIWallpaperService).detach(eq(displayConnector.mToken));
     }
 
@@ -848,10 +891,127 @@
         // GIVEN there are two displays: DEFAULT_DISPLAY, 2
         final int testDisplayId = 2;
         setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+        // GIVEN wallpaper connections have been established for display ID, 2.
+        WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
+                WallpaperManagerInternal.class);
+        wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
+        // Save displayConnectors for display ID, 2, before display removal.
+        WallpaperManagerService.DisplayConnector systemWallpaperDisplayConnector =
+                systemWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
+        WallpaperManagerService.DisplayConnector lockWallpaperDisplayConnector =
+                lockWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
+
+        // WHEN display ID, 2, is removed.
+        DisplayListener displayListener = displayListenerCaptor.getValue();
+        displayListener.onDisplayRemoved(testDisplayId);
+
+        // Then the system wallpaper connection for display ID, 2, is detached.
+        verify(mockIWallpaperService).detach(eq(systemWallpaperDisplayConnector.mToken));
+        // Then the lock wallpaper connection for display ID, 2, is detached.
+        verify(mockIWallpaperService).detach(eq(lockWallpaperDisplayConnector.mToken));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+    public void displayRemoved_fallbackWallpaper_shouldDetachFallbackWallpaperService()
+            throws Exception {
+        ArgumentCaptor<DisplayListener> displayListenerCaptor = ArgumentCaptor.forClass(
+                DisplayListener.class);
+        verify(mDisplayManager).registerDisplayListener(displayListenerCaptor.capture(), eq(null));
+        final int testUserId = USER_SYSTEM;
+        mService.switchUser(testUserId, null);
+        IWallpaperService mockIWallpaperService = mock(IWallpaperService.class);
+        mService.mFallbackWallpaper.connection.mService = mockIWallpaperService;
+        // GIVEN there are two displays: DEFAULT_DISPLAY, 2
+        final int testDisplayId = 2;
+        setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+        // GIVEN display ID, 2, is incompatible with the wallpaper.
+        mService.removeWallpaperCompatibleDisplayForTest(testDisplayId);
+        // GIVEN wallpaper connections have been established for display ID, 2.
+        WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
+                WallpaperManagerInternal.class);
+        wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
+        // Save fallback wallpaper displayConnector for display ID, 2, before display removal.
+        WallpaperManagerService.DisplayConnector fallbackWallpaperConnector =
+                mService.mFallbackWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
+
+        // WHEN displayId, 2, is removed.
+        DisplayListener displayListener = displayListenerCaptor.getValue();
+        displayListener.onDisplayRemoved(testDisplayId);
+
+        // Then the fallback wallpaper connection for display ID, 2, is detached.
+        verify(mockIWallpaperService).detach(eq(fallbackWallpaperConnector.mToken));
+    }
+    // Verify a secondary display removed ended
+
+     // Test fallback wallpaper after enabling connected display supports.
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+    public void testFallbackWallpaperForConnectedDisplays() {
+        final WallpaperData fallbackData = mService.mFallbackWallpaper;
+
+        assertWithMessage(
+                "The connected wallpaper component should be fallback wallpaper component from "
+                        + "config file.")
+                .that(fallbackData.connection.mWallpaper.getComponent())
+                .isEqualTo(sFallbackWallpaperComponentName);
+        assertWithMessage("Fallback wallpaper should support both lock & system.")
+                .that(fallbackData.mWhich)
+                .isEqualTo(FLAG_LOCK | FLAG_SYSTEM);
+    }
+
+    // Verify a secondary display removes system decorations started
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+    public void displayRemoveSystemDecorations_sameSystemAndLockWallpaper_shouldDetachWallpaperServiceOnce()
+            throws Exception {
+        // GIVEN the same wallpaper used for the lock and system.
+        final int testUserId = USER_SYSTEM;
+        mService.switchUser(testUserId, null);
+        final WallpaperData wallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM, testUserId);
+        IWallpaperService mockIWallpaperService = mock(IWallpaperService.class);
+        wallpaper.connection.mService = mockIWallpaperService;
+        // GIVEN there are two displays: DEFAULT_DISPLAY, 2
+        final int testDisplayId = 2;
+        setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
         // GIVEN wallpaper connections have been established for displayID, 2.
         WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
                 WallpaperManagerInternal.class);
-        wallpaperManagerInternal.onDisplayReady(testDisplayId);
+        wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
+        // Save displayConnector for displayId 2 before display removal.
+        WallpaperManagerService.DisplayConnector displayConnector =
+                wallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
+
+        // WHEN displayId, 2, is removed.
+        wallpaperManagerInternal.onDisplayRemoveSystemDecorations(testDisplayId);
+
+        // Then the wallpaper connection for displayId, 2, is detached.
+        verify(mockIWallpaperService).detach(eq(displayConnector.mToken));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+    public void displayRemoveSystemDecorations_differentSystemAndLockWallpapers_shouldDetachWallpaperServiceTwice()
+            throws Exception {
+        // GIVEN different wallpapers used for the lock and system.
+        final int testUserId = USER_SYSTEM;
+        mService.switchUser(testUserId, null);
+        mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
+                FLAG_LOCK, testUserId);
+        final WallpaperData systemWallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM,
+                testUserId);
+        final WallpaperData lockWallpaper = mService.getCurrentWallpaperData(FLAG_LOCK,
+                testUserId);
+        IWallpaperService mockIWallpaperService = mock(IWallpaperService.class);
+        systemWallpaper.connection.mService = mockIWallpaperService;
+        lockWallpaper.connection.mService = mockIWallpaperService;
+        // GIVEN there are two displays: DEFAULT_DISPLAY, 2
+        final int testDisplayId = 2;
+        setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+        // GIVEN wallpaper connections have been established for displayID, 2.
+        WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
+                WallpaperManagerInternal.class);
+        wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
         // Save displayConnectors for displayId 2 before display removal.
         WallpaperManagerService.DisplayConnector systemWallpaperDisplayConnector =
                 systemWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
@@ -859,15 +1019,14 @@
                 lockWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
 
         // WHEN displayId, 2, is removed.
-        DisplayListener displayListener = displayListenerCaptor.getValue();
-        displayListener.onDisplayRemoved(testDisplayId);
+        wallpaperManagerInternal.onDisplayRemoveSystemDecorations(testDisplayId);
 
         // Then the system wallpaper connection for displayId, 2, is detached.
         verify(mockIWallpaperService).detach(eq(systemWallpaperDisplayConnector.mToken));
         // Then the lock wallpaper connection for displayId, 2, is detached.
         verify(mockIWallpaperService).detach(eq(lockWallpaperDisplayConnector.mToken));
     }
-    // Verify a secondary display removed ended
+    // Verify a secondary display removes system decorations ended
 
     // Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
     // non-current user must not bind to wallpaper service.
@@ -893,11 +1052,11 @@
         final WallpaperData lastLockData = mService.mLastLockWallpaper;
         assertWithMessage("Last wallpaper must not be null").that(lastLockData).isNotNull();
         assertWithMessage("Last wallpaper component must be equals.")
-                .that(expectedComponent)
-                .isEqualTo(lastLockData.getComponent());
+                .that(lastLockData.getComponent())
+                .isEqualTo(expectedComponent);
         assertWithMessage("The user id in last wallpaper should be the last switched user")
-                .that(lastUserId)
-                .isEqualTo(lastLockData.userId);
+                .that(lastLockData.userId)
+                .isEqualTo(lastUserId);
         assertWithMessage("Must exist user data connection on last wallpaper data")
                 .that(lastLockData.connection)
                 .isNotNull();
@@ -946,6 +1105,7 @@
             doReturn(mockDisplay).when(mDisplayManager).getDisplay(eq(displayId));
             doReturn(displayId).when(mockDisplay).getDisplayId();
             doReturn(true).when(mockDisplay).hasAccess(anyInt());
+            mService.addWallpaperCompatibleDisplayForTest(displayId);
         }
 
         doReturn(mockDisplays).when(mDisplayManager).getDisplays();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index d427c9d..e94ef5b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -39,6 +39,8 @@
 import android.os.Parcel;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseLongArray;
 
@@ -49,6 +51,7 @@
 import com.android.internal.os.BatteryStatsHistoryIterator;
 import com.android.internal.os.MonotonicClock;
 import com.android.internal.os.PowerProfile;
+import com.android.server.power.optimization.Flags;
 import com.android.server.power.stats.processor.MultiStatePowerAttributor;
 
 import org.junit.Before;
@@ -59,6 +62,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -68,11 +72,14 @@
             .setProvideMainThread(true)
             .build();
 
+    @Rule(order = 1)
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
     private static final long MINUTE_IN_MS = 60 * 1000;
     private static final double PRECISION = 0.00001;
 
-    @Rule(order = 1)
+    @Rule(order = 2)
     public final BatteryUsageStatsRule mStatsRule =
             new BatteryUsageStatsRule(12345)
                     .createTempDirectory()
@@ -868,4 +875,62 @@
 
         stats.close();
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_EXTENDED_BATTERY_HISTORY_CONTINUOUS_COLLECTION_ENABLED)
+    public void testIncludeSubsetOfHistory() throws IOException {
+        MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+        batteryStats.getHistory().setMaxHistoryBufferSize(100);
+        synchronized (batteryStats) {
+            batteryStats.setRecordAllHistoryLocked(true);
+        }
+        batteryStats.forceRecordAllHistory();
+        batteryStats.setNoAutoReset(true);
+
+        long lastIncludedEventTimestamp = 0;
+        String tag = "work work work work work work work work work work work work work work work";
+        for (int i = 1; i < 50; i++) {
+            mStatsRule.advanceTime(TimeUnit.MINUTES.toMillis(9));
+            synchronized (batteryStats) {
+                batteryStats.noteJobStartLocked(tag, 42);
+            }
+            mStatsRule.advanceTime(TimeUnit.MINUTES.toMillis(1));
+            synchronized (batteryStats) {
+                batteryStats.noteJobFinishLocked(tag, 42, 0);
+            }
+            lastIncludedEventTimestamp = mMonotonicClock.monotonicTime();
+        }
+
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+                mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+                mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+                mMonotonicClock);
+
+        BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+                .includeBatteryHistory()
+                .setPreferredHistoryDurationMs(TimeUnit.MINUTES.toMillis(20))
+                .build();
+        final BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats, query);
+        Parcel parcel = Parcel.obtain();
+        stats.writeToParcel(parcel, 0);
+        stats.close();
+
+        parcel.setDataPosition(0);
+        BatteryUsageStats actual = BatteryUsageStats.CREATOR.createFromParcel(parcel);
+
+        long firstIncludedEventTimestamp = 0;
+        try (BatteryStatsHistoryIterator it = actual.iterateBatteryStatsHistory()) {
+            BatteryStats.HistoryItem item;
+            while ((item = it.next()) != null) {
+                if (item.eventCode == BatteryStats.HistoryItem.EVENT_JOB_START) {
+                    firstIncludedEventTimestamp = item.time;
+                    break;
+                }
+            }
+        }
+        actual.close();
+
+        assertThat(firstIncludedEventTimestamp)
+                .isAtLeast(lastIncludedEventTimestamp - TimeUnit.MINUTES.toMillis(30));
+    }
 }
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 9b7bbe0..834fea4 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -113,7 +113,6 @@
     <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.CAMERA" />
-    <uses-permission android:name="android.permission.CREATE_VIRTUAL_DEVICE" />
     <uses-permission android:name="android.permission.MANAGE_KEY_GESTURES" />
 
     <queries>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
index ecc48bf..f55ca0c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
@@ -57,6 +57,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.LocalServices;
+import com.android.server.accessibility.autoclick.AutoclickController;
 import com.android.server.accessibility.gestures.TouchExplorer;
 import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
 import com.android.server.accessibility.magnification.MagnificationGestureHandler;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 5602fb7..9cfa51a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -29,6 +29,7 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 import static android.view.accessibility.Flags.FLAG_SKIP_ACCESSIBILITY_WARNING_DIALOG_FOR_TRUSTED_SERVICES;
 
+import static com.android.input.flags.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
 import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
@@ -38,6 +39,7 @@
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
 import static com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity.EXTRA_TYPE_TO_CHOOSE;
 import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG;
+import static com.android.server.accessibility.Flags.FLAG_ENABLE_MAGNIFICATION_KEYBOARD_CONTROL;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -68,7 +70,6 @@
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.app.admin.DevicePolicyManager;
-import android.app.ecm.EnhancedConfirmationManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -94,6 +95,7 @@
 import android.os.test.FakePermissionEnforcer;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
@@ -585,6 +587,31 @@
     }
 
     @Test
+    @RequiresFlagsEnabled({FLAG_ENABLE_MAGNIFICATION_KEYBOARD_CONTROL, FLAG_KEYBOARD_REPEAT_KEYS})
+    public void testRepeatKeysSettingsChanges_propagateToMagnificationController() {
+        final AccessibilityUserState userState = mA11yms.mUserStates.get(
+                mA11yms.getCurrentUserIdLocked());
+        Settings.Secure.putIntForUser(
+                mTestableContext.getContentResolver(),
+                Settings.Secure.KEY_REPEAT_ENABLED,
+                0, mA11yms.getCurrentUserIdLocked());
+
+        mA11yms.readRepeatKeysSettingsLocked(userState);
+
+        verify(mMockMagnificationController).setRepeatKeysEnabled(false);
+
+        final int timeoutMs = 42;
+        Settings.Secure.putIntForUser(
+                mTestableContext.getContentResolver(),
+                Settings.Secure.KEY_REPEAT_TIMEOUT_MS,
+                timeoutMs, mA11yms.getCurrentUserIdLocked());
+
+        mA11yms.readRepeatKeysSettingsLocked(userState);
+
+        verify(mMockMagnificationController).setRepeatKeysTimeoutMs(timeoutMs);
+    }
+
+    @Test
     public void testSettingsAlwaysOn_setEnabled_featureFlagDisabled_doNothing() {
         when(mMockMagnificationController.isAlwaysOnMagnificationFeatureFlagEnabled())
                 .thenReturn(false);
@@ -2120,23 +2147,6 @@
     }
 
     @Test
-    @EnableFlags({android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED,
-            android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS})
-    public void isAccessibilityTargetAllowed_nonSystemUserId_useEcmWithNonSystemUserId() {
-        String fakePackageName = "FAKE_PACKAGE_NAME";
-        int uid = 0; // uid is not used in the actual implementation when flags are on
-        int userId = mTestableContext.getUserId() + 1234;
-        when(mDevicePolicyManager.getPermittedAccessibilityServices(userId)).thenReturn(
-                List.of(fakePackageName));
-        Context mockUserContext = mock(Context.class);
-        mTestableContext.addMockUserContext(userId, mockUserContext);
-
-        mA11yms.isAccessibilityTargetAllowed(fakePackageName, uid, userId);
-
-        verify(mockUserContext).getSystemService(EnhancedConfirmationManager.class);
-    }
-
-    @Test
     @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
     public void handleKeyGestureEvent_toggleMagnifier() {
         mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index d4f2dcc..5d94e72 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -250,9 +250,9 @@
     }
 
     @Test
-    public void sendGesture_touchableDevice_injectEvents()
-            throws RemoteException {
+    public void sendGesture_touchableDevice_injectEvents_fromAccessibilityTool() {
         when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(true);
+        when(mServiceInfo.isAccessibilityTool()).thenReturn(true);
         setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
         mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
@@ -263,13 +263,31 @@
         mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY);
 
         verify(mMockMotionEventInjector).injectEvents(gestureSteps, mMockServiceClient, 0,
-                Display.DEFAULT_DISPLAY);
+                Display.DEFAULT_DISPLAY, true);
+    }
+
+    @Test
+    public void sendGesture_touchableDevice_injectEvents_fromNonTool() {
+        when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(true);
+        when(mServiceInfo.isAccessibilityTool()).thenReturn(false);
+        setServiceBinding(COMPONENT_NAME);
+        mConnection.bindLocked();
+        mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+
+        ParceledListSlice parceledListSlice = mock(ParceledListSlice.class);
+        List<GestureDescription.GestureStep> gestureSteps = mock(List.class);
+        when(parceledListSlice.getList()).thenReturn(gestureSteps);
+        mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY);
+
+        verify(mMockMotionEventInjector).injectEvents(gestureSteps, mMockServiceClient, 0,
+                Display.DEFAULT_DISPLAY, false);
     }
 
     @Test
     public void sendGesture_untouchableDevice_performGestureResultFailed()
             throws RemoteException {
         when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(false);
+        when(mServiceInfo.isAccessibilityTool()).thenReturn(true);
         setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
         mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
@@ -280,7 +298,7 @@
         mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY);
 
         verify(mMockMotionEventInjector, never()).injectEvents(gestureSteps, mMockServiceClient, 0,
-                Display.DEFAULT_DISPLAY);
+                Display.DEFAULT_DISPLAY, true);
         verify(mMockServiceClient).onPerformGestureResult(0, false);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 233caf9..d2d8c68 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -20,8 +20,7 @@
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_HOVER_MOVE;
 import static android.view.MotionEvent.ACTION_UP;
-import static android.view.WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
-import static android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER;
+import static android.view.accessibility.Flags.FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS;
 
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.anyOf;
@@ -48,10 +47,14 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.Display;
 import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.WindowManagerPolicyConstants;
 import android.view.accessibility.AccessibilityEvent;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -64,6 +67,7 @@
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -77,7 +81,7 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class MotionEventInjectorTest {
-    private static final String LOG_TAG = "MotionEventInjectorTest";
+
     private static final Matcher<MotionEvent> IS_ACTION_DOWN =
             new MotionEventActionMatcher(ACTION_DOWN);
     private static final Matcher<MotionEvent> IS_ACTION_POINTER_DOWN =
@@ -120,6 +124,9 @@
     private static final float POINTER_SIZE = 1;
     private static final int METASTATE = 0;
 
+    @Rule
+    public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     MotionEventInjector mMotionEventInjector;
     IAccessibilityServiceClient mServiceInterface;
     AccessibilityTraceManager mTrace;
@@ -201,7 +208,8 @@
         verifyNoMoreInteractions(next);
         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
 
-        final int expectedFlags = FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY;
+        final int expectedFlags = WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+                | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
         verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), eq(expectedFlags));
         verify(next).onMotionEvent(argThat(mIsLineStart), argThat(mIsLineStart), eq(expectedFlags));
         verifyNoMoreInteractions(next);
@@ -227,6 +235,21 @@
     }
 
     @Test
+    @EnableFlags(FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS)
+    public void testInjectEvents_fromAccessibilityTool_providesToolPolicyFlag() {
+        EventStreamTransformation next = attachMockNext(mMotionEventInjector);
+        injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE,
+                /*fromAccessibilityTool=*/true);
+
+        mMessageCapturingHandler.sendOneMessage(); // Send a motion event
+        verify(next).onMotionEvent(
+                argThat(mIsLineStart), argThat(mIsLineStart),
+                eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+                        | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY
+                        | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL));
+    }
+
+    @Test
     public void testInjectEvents_gestureWithTooManyPoints_shouldNotCrash() throws  Exception {
         int tooManyPointsCount = 20;
         TouchPoint[] startTouchPoints = new TouchPoint[tooManyPointsCount];
@@ -251,14 +274,28 @@
     }
 
     @Test
-    public void testRegularEvent_afterGestureComplete_shouldPassToNext() {
+    @DisableFlags(FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS)
+    public void testRegularEvent_afterGestureComplete_shouldPassToNext_withFlagInjectedFromA11y() {
         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
         reset(next);
         mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
         verify(next).onMotionEvent(argThat(mIsClickDown), argThat(mIsClickDown),
-                eq(FLAG_INJECTED_FROM_ACCESSIBILITY));
+                eq(WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY));
+    }
+
+    @Test
+    @EnableFlags(FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS)
+    public void testRegularEvent_afterGestureComplete_shouldPassToNext_withNoPolicyFlagChanges() {
+        EventStreamTransformation next = attachMockNext(mMotionEventInjector);
+        injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
+        mMessageCapturingHandler.sendAllMessages(); // Send all motion events
+        reset(next);
+        mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
+        verify(next).onMotionEvent(argThat(mIsClickDown), argThat(mIsClickDown),
+                // The regular event passing through the filter should have no policy flag changes
+                eq(0));
     }
 
     @Test
@@ -275,7 +312,8 @@
         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
         verify(next).onMotionEvent(
                 argThat(mIsLineStart), argThat(mIsLineStart),
-                eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY));
+                eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+                        | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY));
     }
 
     @Test
@@ -307,10 +345,12 @@
 
         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
         verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(),
-                eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY));
+                eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+                        | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY));
         verify(next).onMotionEvent(
                 argThat(mIsLineStart), argThat(mIsLineStart),
-                eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY));
+                eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+                        | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY));
     }
 
     @Test
@@ -731,8 +771,14 @@
 
     private void injectEventsSync(List<GestureStep> gestureSteps,
             IAccessibilityServiceClient serviceInterface, int sequence) {
+        injectEventsSync(gestureSteps, serviceInterface, sequence, false);
+    }
+
+    private void injectEventsSync(List<GestureStep> gestureSteps,
+            IAccessibilityServiceClient serviceInterface, int sequence,
+            boolean fromAccessibilityTool) {
         mMotionEventInjector.injectEvents(gestureSteps, serviceInterface, sequence,
-                Display.DEFAULT_DISPLAY);
+                Display.DEFAULT_DISPLAY, fromAccessibilityTool);
         // Dispatch the message sent by the injector. Our simple handler doesn't guarantee stuff
         // happens in order.
         mMessageCapturingHandler.sendLastMessage();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java
rename to services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index acce813..f02bdae 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.accessibility;
+package com.android.server.accessibility.autoclick;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
@@ -39,6 +39,8 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.server.accessibility.AccessibilityTraceManager;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 4ef602f..cd6b36d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -58,9 +58,9 @@
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.test.TestLooper;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
 import android.testing.DexmakerShareClassLoaderRule;
@@ -173,6 +173,8 @@
     @Mock
     private Scroller mMockScroller;
 
+    private TestLooper mTestLooper;
+
     // To mock package-private class
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -199,14 +201,16 @@
 
         mMockResolver = new MockContentResolver();
         mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
-        Looper looper = InstrumentationRegistry.getContext().getMainLooper();
-        // Pretending ID of the Thread associated with looper as main thread ID in controller
-        when(mContext.getMainLooper()).thenReturn(looper);
+        mTestLooper = new TestLooper();
+        when(mContext.getMainLooper()).thenReturn(
+                InstrumentationRegistry.getContext().getMainLooper());
         when(mContext.getContentResolver()).thenReturn(mMockResolver);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         Settings.Secure.putFloatForUser(mMockResolver,
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE,
                 CURRENT_USER_ID);
+        Settings.Secure.putFloatForUser(mMockResolver, Settings.Secure.KEY_REPEAT_ENABLED, 1,
+                CURRENT_USER_ID);
         mScaleProvider = spy(new MagnificationScaleProvider(mContext));
 
         when(mControllerCtx.getContext()).thenReturn(mContext);
@@ -251,7 +255,7 @@
 
         mMagnificationController = spy(new MagnificationController(mService, globalLock, mContext,
                 mScreenMagnificationController, mMagnificationConnectionManager, mScaleProvider,
-                ConcurrentUtils.DIRECT_EXECUTOR));
+                ConcurrentUtils.DIRECT_EXECUTOR, mTestLooper.getLooper()));
         mMagnificationController.setMagnificationCapabilities(
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
 
@@ -261,6 +265,7 @@
 
     @After
     public void tearDown() {
+        mTestLooper.dispatchAll();
         FakeSettingsProvider.clearSettingsProvider();
     }
 
@@ -880,6 +885,145 @@
     }
 
     @Test
+    public void magnificationCallbacks_scaleMagnificationContinuous() throws RemoteException {
+        setMagnificationEnabled(MODE_FULLSCREEN);
+        float currentScale = 2.0f;
+        mMagnificationController.onPerformScaleAction(TEST_DISPLAY, currentScale, false);
+        reset(mScreenMagnificationController);
+
+        float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+        // Start zooming in using keyboard callbacks.
+        mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY,
+                MagnificationController.ZOOM_DIRECTION_IN);
+
+        // The center is unchanged.
+        float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+        expect.that(currentCenterX).isWithin(1.0f).of(newCenterX);
+        expect.that(currentCenterY).isEqualTo(newCenterY);
+
+        // The scale is increased.
+        float newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+        expect.that(currentScale).isLessThan(newScale);
+        currentScale = newScale;
+
+        // Wait for the initial delay to occur.
+        advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1);
+
+        // It should have scaled again after the handler was triggered.
+        newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+        expect.that(currentScale).isLessThan(newScale);
+        currentScale = newScale;
+
+        for (int i = 0; i < 3; i++) {
+            // Wait for repeat delay to occur.
+            advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+            // It should have scaled another time.
+            newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+            expect.that(currentScale).isLessThan(newScale);
+            currentScale = newScale;
+        }
+
+        // Stop magnification scale.
+        mMagnificationController.onScaleMagnificationStop(TEST_DISPLAY,
+                MagnificationController.ZOOM_DIRECTION_IN);
+
+        // It should not scale again, even after the appropriate delay.
+        advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+        newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+        expect.that(currentScale).isEqualTo(newScale);
+    }
+
+    @Test
+    public void magnificationCallbacks_panMagnificationContinuous_repeatKeysTimeout200()
+            throws RemoteException {
+        // Shorter than default.
+        testMagnificationContinuousPanningWithTimeout(200);
+    }
+
+    @Test
+    public void magnificationCallbacks_panMagnificationContinuous_repeatKeysTimeout1000()
+            throws RemoteException {
+        // Longer than default.
+        testMagnificationContinuousPanningWithTimeout(1000);
+    }
+
+    @Test
+    public void magnificationCallbacks_panMagnification_notContinuousWithRepeatKeysDisabled()
+            throws RemoteException {
+        mMagnificationController.setRepeatKeysEnabled(false);
+        setMagnificationEnabled(MODE_FULLSCREEN);
+        mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 4.0f, false);
+        reset(mScreenMagnificationController);
+
+        float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+        // Start moving down using keyboard callbacks.
+        mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_DOWN);
+
+        float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+        expect.that(currentCenterY).isLessThan(newCenterY);
+        expect.that(currentCenterX).isEqualTo(newCenterX);
+
+        currentCenterX = newCenterX;
+        currentCenterY = newCenterY;
+
+        for (int i = 0; i < 3; i++) {
+            // Wait for the initial delay to occur.
+            advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1);
+
+            // It should not have moved again because repeat keys is disabled.
+            newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+            newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+            expect.that(currentCenterX).isEqualTo(newCenterX);
+            expect.that(currentCenterY).isEqualTo(newCenterY);
+            currentCenterX = newCenterX;
+            currentCenterY = newCenterY;
+        }
+
+        mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_DOWN);
+    }
+
+    @Test
+    public void magnificationCallbacks_scaleMagnification_notContinuousWithRepeatKeysDisabled()
+            throws RemoteException {
+        mMagnificationController.setRepeatKeysEnabled(false);
+        setMagnificationEnabled(MODE_FULLSCREEN);
+        float currentScale = 8.0f;
+        mMagnificationController.onPerformScaleAction(TEST_DISPLAY, currentScale, false);
+        reset(mScreenMagnificationController);
+
+        // Start scaling out using keyboard callbacks.
+        mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY,
+                MagnificationController.ZOOM_DIRECTION_OUT);
+
+        float newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+        expect.that(currentScale).isGreaterThan(newScale);
+
+        currentScale = newScale;
+
+        for (int i = 0; i < 3; i++) {
+            // Wait for the initial delay to occur.
+            advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1);
+
+            // It should not have scaled again because repeat keys is disabled.
+            newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+            expect.that(currentScale).isEqualTo(newScale);
+        }
+
+        mMagnificationController.onScaleMagnificationStop(TEST_DISPLAY,
+                MagnificationController.ZOOM_DIRECTION_OUT);
+    }
+
+    @Test
     public void enableWindowMode_notifyMagnificationChanged() throws RemoteException {
         setMagnificationEnabled(MODE_WINDOW);
 
@@ -1196,7 +1340,8 @@
         assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, lastActivatedMode);
     }
 
-    @Test public void activateFullScreenMagnification_triggerCallback() throws RemoteException {
+    @Test
+    public void activateFullScreenMagnification_triggerCallback() throws RemoteException {
         setMagnificationEnabled(MODE_FULLSCREEN);
         verify(mMagnificationController).onFullScreenMagnificationActivationState(
                 eq(TEST_DISPLAY), eq(true));
@@ -1573,8 +1718,8 @@
         float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
         float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
 
-        // Move right.
-        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+        // Move right using keyboard callbacks.
+        mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
                 MagnificationController.PAN_DIRECTION_RIGHT);
         float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
         float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
@@ -1582,11 +1727,13 @@
         expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
         expect.that(currentCenterY).isEqualTo(newCenterY);
 
+        mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_RIGHT);
         currentCenterX = newCenterX;
         currentCenterY = newCenterY;
 
         // Move left.
-        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+        mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
                 MagnificationController.PAN_DIRECTION_LEFT);
         newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
         newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
@@ -1594,11 +1741,13 @@
         expect.that(currentCenterX - newCenterX).isWithin(0.01f).of(expectedStep);
         expect.that(currentCenterY).isEqualTo(newCenterY);
 
+        mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_LEFT);
         currentCenterX = newCenterX;
         currentCenterY = newCenterY;
 
         // Move down.
-        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+        mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
                 MagnificationController.PAN_DIRECTION_DOWN);
         newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
         newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
@@ -1606,17 +1755,22 @@
         expect.that(currentCenterY).isLessThan(newCenterY);
         expect.that(newCenterY - currentCenterY).isWithin(0.1f).of(expectedStep);
 
+        mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_DOWN);
         currentCenterX = newCenterX;
         currentCenterY = newCenterY;
 
         // Move up.
-        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+        mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
                 MagnificationController.PAN_DIRECTION_UP);
         newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
         newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
         expect.that(currentCenterX).isEqualTo(newCenterX);
         expect.that(currentCenterY).isGreaterThan(newCenterY);
         expect.that(currentCenterY - newCenterY).isWithin(0.01f).of(expectedStep);
+
+        mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_UP);
     }
 
     private void testWindowMagnificationPanWithStepSize(float expectedStepDip)
@@ -1626,28 +1780,110 @@
         final float expectedStep = expectedStepDip * metrics.density;
 
         // Move right.
-        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+        mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
                 MagnificationController.PAN_DIRECTION_RIGHT);
         verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
                 floatThat(step -> Math.abs(step - expectedStep) < 0.0001), eq(0.0f));
+        mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_RIGHT);
 
         // Move left.
-        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+        mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
                 MagnificationController.PAN_DIRECTION_LEFT);
         verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
                 floatThat(step -> Math.abs(expectedStep - step) < 0.0001), eq(0.0f));
+        mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_LEFT);
 
         // Move down.
-        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+        mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
                 MagnificationController.PAN_DIRECTION_DOWN);
         verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
                 eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001));
+        mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_DOWN);
 
         // Move up.
-        mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+        mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
                 MagnificationController.PAN_DIRECTION_UP);
         verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
                 eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001));
+        mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_UP);
+    }
+
+    private void
+            testMagnificationContinuousPanningWithTimeout(int timeoutMs) throws RemoteException {
+        mMagnificationController.setRepeatKeysTimeoutMs(timeoutMs);
+        expect.that(timeoutMs).isEqualTo(
+                mMagnificationController.getInitialKeyboardRepeatIntervalMs());
+
+        setMagnificationEnabled(MODE_FULLSCREEN);
+        mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false);
+        reset(mScreenMagnificationController);
+
+        DisplayMetrics metrics = new DisplayMetrics();
+        mDisplay.getMetrics(metrics);
+        float expectedStep = 27 * metrics.density;
+
+        float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+        // Start moving right using keyboard callbacks.
+        mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_RIGHT);
+
+        float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+        expect.that(currentCenterX).isLessThan(newCenterX);
+        expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+        expect.that(currentCenterY).isEqualTo(newCenterY);
+
+        currentCenterX = newCenterX;
+        currentCenterY = newCenterY;
+
+        // Wait for the initial delay to occur.
+        advanceTime(timeoutMs + 1);
+
+        // It should have moved again after the handler was triggered.
+        newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+        expect.that(currentCenterX).isLessThan(newCenterX);
+        expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+        expect.that(currentCenterY).isEqualTo(newCenterY);
+        currentCenterX = newCenterX;
+        currentCenterY = newCenterY;
+
+        for (int i = 0; i < 3; i++) {
+            // Wait for repeat delay to occur.
+            advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+            // It should have moved another time.
+            newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+            newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+            expect.that(currentCenterX).isLessThan(newCenterX);
+            expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+            expect.that(currentCenterY).isEqualTo(newCenterY);
+            currentCenterX = newCenterX;
+            currentCenterY = newCenterY;
+        }
+
+        // Stop magnification pan.
+        mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+                MagnificationController.PAN_DIRECTION_RIGHT);
+
+        // It should not move again, even after the appropriate delay.
+        advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+        newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+        newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+        expect.that(newCenterX).isEqualTo(currentCenterX);
+        expect.that(newCenterY).isEqualTo(currentCenterY);
+    }
+
+    private void advanceTime(long timeMs) {
+        mTestLooper.moveTimeForward(timeMs);
+        mTestLooper.dispatchAll();
     }
 
     private static class WindowMagnificationMgrCallbackDelegate implements
diff --git a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
index ef9580c..cbe2bfb 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
@@ -28,8 +28,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.Manifest;
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
@@ -38,6 +40,7 @@
 import android.media.AudioSystem;
 import android.media.IAudioDeviceVolumeDispatcher;
 import android.media.VolumeInfo;
+import android.os.IpcDataCache;
 import android.os.PermissionEnforcer;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
@@ -83,6 +86,7 @@
 
     @Before
     public void setUp() throws Exception {
+        IpcDataCache.disableForTestMode();
         mTestLooper = new TestLooper();
 
         mContext = spy(ApplicationProvider.getApplicationContext());
@@ -93,6 +97,10 @@
         when(mResources.getBoolean(com.android.internal.R.bool.config_useFixedVolume))
                 .thenReturn(false);
 
+        when(mContext.checkCallingOrSelfPermission(
+                 Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED))
+                 .thenReturn(PackageManager.PERMISSION_GRANTED);
+
         mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
         mSystemServer = new NoOpSystemServerAdapter();
         mSettingsAdapter = new NoOpSettingsAdapter();
diff --git a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
index 746645a..541dbba 100644
--- a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
@@ -28,6 +28,7 @@
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.IDeviceVolumeBehaviorDispatcher;
+import android.os.IpcDataCache;
 import android.os.PermissionEnforcer;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
@@ -69,6 +70,7 @@
 
     @Before
     public void setUp() throws Exception {
+        IpcDataCache.disableForTestMode();
         mTestLooper = new TestLooper();
         mContext = InstrumentationRegistry.getTargetContext();
         mAudioSystem = new NoOpAudioSystemAdapter();
diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
index 6b41c43..0bbae24 100644
--- a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
@@ -80,6 +80,7 @@
 import android.media.IDeviceVolumeBehaviorDispatcher;
 import android.media.VolumeInfo;
 import android.media.audiopolicy.AudioVolumeGroup;
+import android.os.IpcDataCache;
 import android.os.Looper;
 import android.os.PermissionEnforcer;
 import android.os.test.TestLooper;
@@ -210,6 +211,8 @@
 
     @Before
     public void setUp() throws Exception {
+        IpcDataCache.disableForTestMode();
+
         mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
         mTestLooper = new TestLooper();
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 605fed0..c7efa31 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -45,7 +45,6 @@
 import android.content.res.Resources;
 import android.hardware.biometrics.AuthenticationStateListener;
 import android.hardware.biometrics.BiometricManager;
-import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -504,23 +503,9 @@
                 eq(callback));
     }
 
-    @Test(expected = UnsupportedOperationException.class)
-    public void testGetLastAuthenticationTime_flaggedOff_throwsUnsupportedOperationException()
-            throws Exception {
-        mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
-        setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
-
-        mAuthService = new AuthService(mContext, mInjector);
-        mAuthService.onStart();
-
-        mAuthService.mImpl.getLastAuthenticationTime(0,
-                BiometricManager.Authenticators.BIOMETRIC_STRONG);
-    }
-
     @Test
-    public void testGetLastAuthenticationTime_flaggedOn_callsBiometricService()
+    public void testGetLastAuthenticationTime_callsBiometricService()
             throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
         setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
 
         mAuthService = new AuthService(mContext, mInjector);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 8b9def9..d8a6162 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -18,9 +18,11 @@
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CONTENT_VIEW_MORE_OPTIONS_BUTTON;
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON;
 import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED;
 import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED;
+import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS;
 import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_NEGATIVE;
 import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_USER_CANCEL;
 
@@ -588,7 +590,7 @@
     }
 
     @Test
-    public void testLogOnDialogDismissed_error() throws RemoteException {
+    public void testLogOnDialogDismissed_negativeButton() throws RemoteException {
         final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
 
         setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
@@ -615,6 +617,33 @@
     }
 
     @Test
+    public void testLogOnDialogDismissed_contentViewMoreOptionsButton() throws RemoteException {
+        final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
+
+        setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
+        final AuthSession session = createAuthSession(mSensors,
+                false /* checkDevicePolicyManager */,
+                Authenticators.BIOMETRIC_STRONG,
+                TEST_REQUEST_ID,
+                0 /* operationId */,
+                0 /* userId */);
+        session.goToInitialState();
+        assertEquals(STATE_AUTH_CALLED, session.getState());
+
+        session.onDialogDismissed(DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS, null);
+        verify(mBiometricFrameworkStatsLogger, times(1)).error(
+                (OperationContextExt) anyObject(),
+                eq(BiometricsProtoEnums.MODALITY_FACE),
+                eq(BiometricsProtoEnums.ACTION_AUTHENTICATE),
+                eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
+                eq(false),
+                anyLong(),
+                eq(BIOMETRIC_ERROR_CONTENT_VIEW_MORE_OPTIONS_BUTTON),
+                eq(0) /* vendorCode */,
+                eq(0) /* userId */);
+    }
+
+    @Test
     public void onErrorReceivedAfterOnTryAgainPressedWhenSensorsAuthenticating() throws Exception {
         setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
         setupFace(1 /* id */, false, mock(IBiometricAuthenticator.class));
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index acca4cc..9918a9a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -2014,20 +2014,9 @@
         verifyNoMoreInteractions(callback);
     }
 
-    @Test(expected = UnsupportedOperationException.class)
-    public void testGetLastAuthenticationTime_flagOff_throwsUnsupportedOperationException()
-            throws RemoteException {
-        mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
-
-        mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
-        mBiometricService.mImpl.getLastAuthenticationTime(0, Authenticators.BIOMETRIC_STRONG);
-    }
-
     @Test
-    public void testGetLastAuthenticationTime_flagOn_callsKeystoreAuthorization()
+    public void testGetLastAuthenticationTime_callsKeystoreAuthorization()
             throws RemoteException {
-        mSetFlagsRule.enableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
-
         final int[] hardwareAuthenticators = new int[] {
                 HardwareAuthenticatorType.PASSWORD,
                 HardwareAuthenticatorType.FINGERPRINT
diff --git a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
index 57f3cc0..515d8e9 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
@@ -51,20 +51,22 @@
 
     @Test
     public void getStoredProviders_emptyValue_success() {
-        Set<String> providers = CredentialManagerService.getStoredProviders("", "");
+        Set<String> providers = CredentialManagerService.getStoredProvidersExceptPackage(
+                "", "");
         assertThat(providers.size()).isEqualTo(0);
     }
 
     @Test
     public void getStoredProviders_nullValue_success() {
-        Set<String> providers = CredentialManagerService.getStoredProviders(null, null);
+        Set<String> providers = CredentialManagerService.getStoredProvidersExceptPackage(
+                null, null);
         assertThat(providers.size()).isEqualTo(0);
     }
 
     @Test
     public void getStoredProviders_success() {
         Set<String> providers =
-                CredentialManagerService.getStoredProviders(
+                CredentialManagerService.getStoredProvidersExceptPackage(
                         "com.example.test/.TestActivity:com.example.test/.TestActivity2:"
                                 + "com.example.test2/.TestActivity:blank",
                         "com.example.test");
@@ -74,6 +76,7 @@
 
     @Test
     public void onProviderRemoved_success() {
+        int userId = UserHandle.myUserId();
         setSettingsKey(
                 Settings.Secure.AUTOFILL_SERVICE,
                 CredentialManagerService.AUTOFILL_PLACEHOLDER_VALUE);
@@ -85,7 +88,7 @@
                 "com.example.test/com.example.test.TestActivity");
 
         CredentialManagerService.updateProvidersWhenPackageRemoved(
-                mSettingsWrapper, "com.example.test");
+                mSettingsWrapper, "com.example.test", userId);
 
         assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)).isEqualTo("");
         assertThat(getSettingsKey(Settings.Secure.CREDENTIAL_SERVICE))
@@ -95,6 +98,7 @@
 
     @Test
     public void onProviderRemoved_notPrimaryRemoved_success() {
+        int userId = UserHandle.myUserId();
         final String testCredentialPrimaryValue = "com.example.test/com.example.test.TestActivity";
         final String testCredentialValue =
                 "com.example.test/com.example.test.TestActivity:com.example.test2/com.example.test2.TestActivity";
@@ -106,7 +110,7 @@
         setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, testCredentialPrimaryValue);
 
         CredentialManagerService.updateProvidersWhenPackageRemoved(
-                mSettingsWrapper, "com.example.test3");
+                mSettingsWrapper, "com.example.test3", userId);
 
         // Since the provider removed was not a primary provider then we should do nothing.
         assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE))
@@ -120,6 +124,7 @@
 
     @Test
     public void onProviderRemoved_isAlsoAutofillProvider_success() {
+        int userId = UserHandle.myUserId();
         setSettingsKey(
                 Settings.Secure.AUTOFILL_SERVICE,
                 "com.example.test/com.example.test.AutofillProvider");
@@ -131,7 +136,7 @@
                 "com.example.test/com.example.test.TestActivity");
 
         CredentialManagerService.updateProvidersWhenPackageRemoved(
-                mSettingsWrapper, "com.example.test");
+                mSettingsWrapper, "com.example.test", userId);
 
         assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)).isEqualTo("");
         assertThat(getSettingsKey(Settings.Secure.CREDENTIAL_SERVICE))
@@ -141,6 +146,7 @@
 
     @Test
     public void onProviderRemoved_notPrimaryRemoved_isAlsoAutofillProvider_success() {
+        int userId = UserHandle.myUserId();
         final String testCredentialPrimaryValue = "com.example.test/com.example.test.TestActivity";
         final String testCredentialValue =
                 "com.example.test/com.example.test.TestActivity:com.example.test2/com.example.test2.TestActivity";
@@ -151,7 +157,7 @@
         setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, testCredentialPrimaryValue);
 
         CredentialManagerService.updateProvidersWhenPackageRemoved(
-                mSettingsWrapper, "com.example.test3");
+                mSettingsWrapper, "com.example.test3", userId);
 
         // Since the provider removed was not a primary provider then we should do nothing.
         assertCredentialPropertyEquals(
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 3ced56a..a58a9cd 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -34,7 +34,6 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -53,15 +52,11 @@
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
-import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions.LaunchCookie;
 import android.app.AppOpsManager;
-import android.app.Instrumentation;
 import android.app.KeyguardManager;
-import android.app.role.RoleManager;
-import android.companion.AssociationRequest;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -76,11 +71,9 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.test.TestLooper;
-import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
@@ -99,7 +92,6 @@
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -110,7 +102,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -292,8 +283,6 @@
         assertThat(stoppedCallback2).isFalse();
     }
 
-    @EnableFlags(android.companion.virtualdevice.flags
-            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
     public void testCreateProjection_keyguardLocked() throws Exception {
         MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
@@ -308,8 +297,6 @@
         assertThat(mIMediaProjectionCallback.mLatch.await(5, TimeUnit.SECONDS)).isTrue();
     }
 
-    @EnableFlags(android.companion.virtualdevice.flags
-            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
     public void testCreateProjection_keyguardLocked_packageAllowlisted()
             throws NameNotFoundException {
@@ -325,8 +312,6 @@
         assertThat(mService.getActiveProjectionInfo()).isNotNull();
     }
 
-    @EnableFlags(android.companion.virtualdevice.flags
-            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
     public void testCreateProjection_keyguardLocked_AppOpMediaProjection()
             throws NameNotFoundException {
@@ -347,50 +332,6 @@
         assertThat(mService.getActiveProjectionInfo()).isNotNull();
     }
 
-    @EnableFlags(android.companion.virtualdevice.flags
-            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
-    @Test
-    public void testCreateProjection_keyguardLocked_RoleHeld() {
-        runWithRole(
-                AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
-                () -> {
-                    try {
-                        mAppInfo.privateFlags |= PRIVATE_FLAG_PRIVILEGED;
-                        doReturn(mAppInfo)
-                                .when(mPackageManager)
-                                .getApplicationInfoAsUser(
-                                        anyString(),
-                                        any(ApplicationInfoFlags.class),
-                                        any(UserHandle.class));
-                        MediaProjectionManagerService.MediaProjection projection =
-                                mService.createProjectionInternal(
-                                        Process.myUid(),
-                                        mContext.getPackageName(),
-                                        TYPE_MIRRORING,
-                                        /* isPermanentGrant= */ false,
-                                        UserHandle.CURRENT,
-                                        DEFAULT_DISPLAY);
-                        doReturn(true).when(mKeyguardManager).isKeyguardLocked();
-                        doReturn(PackageManager.PERMISSION_DENIED)
-                                .when(mPackageManager)
-                                .checkPermission(RECORD_SENSITIVE_CONTENT, projection.packageName);
-
-                        projection.start(mIMediaProjectionCallback);
-                        projection.notifyVirtualDisplayCreated(10);
-
-                        // The projection was started because it was allowed to capture the
-                        // keyguard.
-                        assertWithMessage("Failed to run projection")
-                                .that(mService.getActiveProjectionInfo())
-                                .isNotNull();
-                    } catch (NameNotFoundException e) {
-                        throw new RuntimeException(e);
-                    }
-                });
-    }
-
-    @EnableFlags(android.companion.virtualdevice.flags
-            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
     public void testCreateProjection_keyguardLocked_screenshareProtectionsDisabled()
             throws NameNotFoundException {
@@ -416,8 +357,6 @@
         }
     }
 
-    @EnableFlags(android.companion.virtualdevice.flags
-            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
     public void testCreateProjection_keyguardLocked_noDisplayCreated()
             throws NameNotFoundException {
@@ -509,8 +448,6 @@
         assertThat(secondProjection).isNotEqualTo(projection);
     }
 
-    @EnableFlags(android.companion.virtualdevice.flags
-            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
     public void testReuseProjection_keyguardNotLocked_startConsentDialog()
             throws NameNotFoundException {
@@ -527,8 +464,6 @@
         verify(mContext).startActivityAsUser(any(), any());
     }
 
-    @EnableFlags(android.companion.virtualdevice.flags
-            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
     public void testReuseProjection_keyguardLocked_noConsentDialog() throws NameNotFoundException {
         MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
@@ -1302,48 +1237,6 @@
         return mService.getProjectionInternal(UID, PACKAGE_NAME);
     }
 
-    /**
-     * Run the provided block giving the current context's package the provided role.
-     */
-    @SuppressWarnings("SameParameterValue")
-    private void runWithRole(String role, Runnable block) {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        String packageName = mContext.getPackageName();
-        UserHandle user = instrumentation.getTargetContext().getUser();
-        RoleManager roleManager = Objects.requireNonNull(
-                mContext.getSystemService(RoleManager.class));
-        try {
-            CountDownLatch latch = new CountDownLatch(1);
-            instrumentation.getUiAutomation().adoptShellPermissionIdentity(
-                    Manifest.permission.MANAGE_ROLE_HOLDERS,
-                    Manifest.permission.BYPASS_ROLE_QUALIFICATION);
-
-            roleManager.setBypassingRoleQualification(true);
-            roleManager.addRoleHolderAsUser(role, packageName,
-                    /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
-                    mContext.getMainExecutor(), success -> {
-                        if (success) {
-                            latch.countDown();
-                        } else {
-                            Assert.fail("Couldn't set role for test (failure) " + role);
-                        }
-                    });
-            assertWithMessage("Couldn't set role for test (timeout) : " + role)
-                    .that(latch.await(1, TimeUnit.SECONDS)).isTrue();
-            block.run();
-
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        } finally {
-            roleManager.removeRoleHolderAsUser(role, packageName,
-                    /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
-                    mContext.getMainExecutor(), (aBool) -> {});
-            roleManager.setBypassingRoleQualification(false);
-            instrumentation.getUiAutomation()
-                    .dropShellPermissionIdentity();
-        }
-    }
-
     private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
         CountDownLatch mLatch = new CountDownLatch(1);
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
index 379079a..10ac049 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
@@ -22,7 +22,6 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -37,13 +36,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
-import android.app.Instrumentation;
 import android.app.KeyguardManager;
-import android.app.role.RoleManager;
 import android.companion.AssociationRequest;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -69,7 +65,6 @@
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -79,9 +74,7 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
@@ -123,6 +116,8 @@
     private KeyguardManager mKeyguardManager;
     @Mock
     private TelecomManager mTelecomManager;
+    @Mock
+    private MediaProjectionStopController.RoleHolderProvider mRoleManager;
 
     private AppOpsManager mAppOpsManager;
     @Mock
@@ -145,7 +140,7 @@
         mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
         mContext.setMockPackageManager(mPackageManager);
 
-        mStopController = new MediaProjectionStopController(mContext, mStopConsumer);
+        mStopController = new MediaProjectionStopController(mContext, mStopConsumer, mRoleManager);
         mService = new MediaProjectionManagerService(mContext,
                 mMediaProjectionMetricsLoggerInjector);
 
@@ -170,8 +165,6 @@
     }
 
     @Test
-    @EnableFlags(
-            android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     public void testMediaProjectionNotRestricted() throws Exception {
         when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
 
@@ -180,8 +173,6 @@
     }
 
     @Test
-    @EnableFlags(
-            android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     public void testMediaProjectionRestricted() throws Exception {
         MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
         mediaProjection.notifyVirtualDisplayCreated(1);
@@ -239,21 +230,13 @@
 
     @Test
     public void testExemptFromStoppingHasAppStreamingRole() throws Exception {
-        runWithRole(
-                AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
-                () -> {
-                    try {
-                        MediaProjectionManagerService.MediaProjection mediaProjection =
-                                createMediaProjection();
-                        doReturn(PackageManager.PERMISSION_DENIED).when(
-                                mPackageManager).checkPermission(
-                                RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
-                        assertThat(mStopController.isExemptFromStopping(mediaProjection,
-                                MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+        doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+                RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+        doReturn(List.of(mediaProjection.packageName)).when(mRoleManager).getRoleHoldersAsUser(
+                eq(AssociationRequest.DEVICE_PROFILE_APP_STREAMING), any(UserHandle.class));
+        assertThat(mStopController.isExemptFromStopping(mediaProjection,
+                MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
     }
 
     @Test
@@ -316,8 +299,6 @@
     }
 
     @Test
-    @EnableFlags(
-            android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     public void testKeyguardLockedStateChanged_unlocked() {
         mStopController.onKeyguardLockedStateChanged(false);
 
@@ -325,8 +306,6 @@
     }
 
     @Test
-    @EnableFlags(
-            android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     public void testKeyguardLockedStateChanged_locked() {
         mStopController.onKeyguardLockedStateChanged(true);
 
@@ -438,47 +417,4 @@
                 MediaProjectionManager.TYPE_SCREEN_CAPTURE, false, mContext.getUser(),
                 INVALID_DISPLAY);
     }
-
-    /**
-     * Run the provided block giving the current context's package the provided role.
-     */
-    @SuppressWarnings("SameParameterValue")
-    private void runWithRole(String role, Runnable block) {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        String packageName = mContext.getPackageName();
-        UserHandle user = instrumentation.getTargetContext().getUser();
-        RoleManager roleManager = Objects.requireNonNull(
-                mContext.getSystemService(RoleManager.class));
-        try {
-            CountDownLatch latch = new CountDownLatch(1);
-            instrumentation.getUiAutomation().adoptShellPermissionIdentity(
-                    Manifest.permission.MANAGE_ROLE_HOLDERS,
-                    Manifest.permission.BYPASS_ROLE_QUALIFICATION);
-
-            roleManager.setBypassingRoleQualification(true);
-            roleManager.addRoleHolderAsUser(role, packageName,
-                    /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
-                    mContext.getMainExecutor(), success -> {
-                        if (success) {
-                            latch.countDown();
-                        } else {
-                            Assert.fail("Couldn't set role for test (failure) " + role);
-                        }
-                    });
-            assertWithMessage("Couldn't set role for test (timeout) : " + role)
-                    .that(latch.await(1, TimeUnit.SECONDS)).isTrue();
-            block.run();
-
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        } finally {
-            roleManager.removeRoleHolderAsUser(role, packageName,
-                    /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
-                    mContext.getMainExecutor(), (aBool) -> {
-                    });
-            roleManager.setBypassingRoleQualification(false);
-            instrumentation.getUiAutomation()
-                    .dropShellPermissionIdentity();
-        }
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/OWNERS b/services/tests/servicestests/src/com/android/server/media/projection/OWNERS
index 832bcd9..3caf7fa 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/media/projection/OWNERS
@@ -1 +1,2 @@
+# Bug component: 1345447
 include /media/java/android/media/projection/OWNERS
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 2f3bca0..fbb673a 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
@@ -1762,7 +1762,8 @@
                     /* rankingAdjustment= */ 0,
                     /* isBubble= */ false,
                     /* proposedImportance= */ 0,
-                    /* sensitiveContent= */ false
+                    /* sensitiveContent= */ false,
+                    /* summarization = */ null
             );
             return true;
         }).when(mRankingMap).getRanking(eq(key),
@@ -1806,7 +1807,8 @@
                     /* rankingAdjustment= */ 0,
                     /* isBubble= */ false,
                     /* proposedImportance= */ 0,
-                    /* sensitiveContent= */ false
+                    /* sensitiveContent= */ false,
+                    /* summarization = */ null
             );
             return true;
         }).when(mRankingMap).getRanking(eq(CUSTOM_KEY),
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index 5a89db1..4241aa9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -109,7 +109,7 @@
         assertEquals(3, mService.mMaxUpdatesPerInterval);
     }
 
-    public void testRestConfig() throws Exception {
+    public void disabled_testRestConfig() throws Exception {
         mService.mMaxUpdatesPerInterval = 99;
 
         mInjectedCallingUid = Process.SHELL_UID;
@@ -223,7 +223,7 @@
     }
 
     // This command is deprecated. Will remove the test later.
-    public void testLauncherCommands() throws Exception {
+    public void disabled_testLauncherCommands() throws Exception {
         prepareGetRoleHoldersAsUser(getSystemLauncher().activityInfo.packageName, USER_10);
         prepareGetHomeActivitiesAsUser(
                 /* preferred */ getSystemLauncher().activityInfo.getComponentName(),
@@ -401,7 +401,7 @@
 
     }
 
-    public void testDumpsysArgs() {
+    public void disabled_testDumpsysArgs() {
         checkDumpsysArgs(null, true, false, false);
         checkDumpsysArgs(array("-u"), true, true, false);
         checkDumpsysArgs(array("--uid"), true, true, false);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index 5c2132f..19c26f0b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -91,7 +91,7 @@
         assertEquals(USER_10, mService.getParentOrSelfUserId(USER_P0));
     }
 
-    public void testIsRequestPinShortcutSupported() {
+    public void disabled_testIsRequestPinShortcutSupported() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
         setDefaultLauncher(USER_11, LAUNCHER_2);
 
@@ -168,7 +168,7 @@
         });
     }
 
-    public void testRequestPinShortcut_notSupported() {
+    public void disabled_testRequestPinShortcut_notSupported() {
         // User-0's launcher has no confirmation activity.
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
@@ -322,7 +322,7 @@
         });
     }
 
-    public void testRequestPinShortcut() {
+    public void disabled_testRequestPinShortcut() {
         checkRequestPinShortcut(/* resultIntent=*/ null);
     }
 
@@ -332,11 +332,11 @@
                 PendingIntent.FLAG_MUTABLE).getIntentSender();
     }
 
-    public void testRequestPinShortcut_withCallback() {
+    public void disabled_testRequestPinShortcut_withCallback() {
         checkRequestPinShortcut(makeResultIntent());
     }
 
-    public void testRequestPinShortcut_explicitTargetActivity() {
+    public void disabled_testRequestPinShortcut_explicitTargetActivity() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
         setDefaultLauncher(USER_11, LAUNCHER_2);
 
@@ -410,7 +410,7 @@
         });
     }
 
-    public void testRequestPinShortcut_noTargetActivity_noMainActivity() {
+    public void disabled_testRequestPinShortcut_noTargetActivity_noMainActivity() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
         setDefaultLauncher(USER_11, LAUNCHER_2);
 
@@ -475,7 +475,7 @@
 
     }
 
-    public void testRequestPinShortcut_dynamicExists() {
+    public void disabled_testRequestPinShortcut_dynamicExists() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -590,7 +590,7 @@
         });
     }
 
-    public void testRequestPinShortcut_dynamicExists_alreadyPinned() {
+    public void disabled_testRequestPinShortcut_dynamicExists_alreadyPinned() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -675,7 +675,7 @@
         });
     }
 
-    public void testRequestPinShortcut_manifestExists_alreadyPinned() {
+    public void disabled_testRequestPinShortcut_manifestExists_alreadyPinned() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -757,7 +757,7 @@
         });
     }
 
-    public void testRequestPinShortcut_wasDynamic_alreadyPinned() {
+    public void disabled_testRequestPinShortcut_wasDynamic_alreadyPinned() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -785,7 +785,7 @@
         });
     }
 
-    public void testRequestPinShortcut_wasDynamic_disabled_alreadyPinned() {
+    public void disabled_testRequestPinShortcut_wasDynamic_disabled_alreadyPinned() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -816,7 +816,7 @@
         });
     }
 
-    public void testRequestPinShortcut_wasManifest_alreadyPinned() {
+    public void disabled_testRequestPinShortcut_wasManifest_alreadyPinned() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -848,7 +848,7 @@
         });
     }
 
-    public void testRequestPinShortcut_dynamicExists_alreadyPinnedByAnother() {
+    public void disabled_testRequestPinShortcut_dynamicExists_alreadyPinnedByAnother() {
         // Initially all launchers have the shortcut permission, until we call setDefaultLauncher().
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -910,7 +910,7 @@
         });
     }
 
-    public void testRequestPinShortcut_manifestExists_alreadyPinnedByAnother() {
+    public void disabled_testRequestPinShortcut_manifestExists_alreadyPinnedByAnother() {
         // Initially all launchers have the shortcut permission, until we call setDefaultLauncher().
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1041,7 +1041,7 @@
     /**
      * When trying to pin an existing shortcut, the new fields shouldn't override existing fields.
      */
-    public void testRequestPinShortcut_dynamicExists_titleWontChange() {
+    public void disabled_testRequestPinShortcut_dynamicExists_titleWontChange() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -1106,7 +1106,7 @@
     /**
      * When trying to pin an existing shortcut, the new fields shouldn't override existing fields.
      */
-    public void testRequestPinShortcut_manifestExists_titleWontChange() {
+    public void disabled_testRequestPinShortcut_manifestExists_titleWontChange() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1173,7 +1173,7 @@
      * The dynamic shortcut existed, but before accepting(), it's removed.  Because the request
      * has a partial shortcut, accept() should fail.
      */
-    public void testRequestPinShortcut_dynamicExists_thenRemoved_error() {
+    public void disabled_testRequestPinShortcut_dynamicExists_thenRemoved_error() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1231,7 +1231,7 @@
      * The dynamic shortcut existed, but before accepting(), it's removed.  Because the request
      * has all the mandatory fields, we can go ahead and still publish it.
      */
-    public void testRequestPinShortcut_dynamicExists_thenRemoved_okay() {
+    public void disabled_testRequestPinShortcut_dynamicExists_thenRemoved_okay() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1287,7 +1287,7 @@
      * The manifest shortcut existed, but before accepting(), it's removed.  Because the request
      * has a partial shortcut, accept() should fail.
      */
-    public void testRequestPinShortcut_manifestExists_thenRemoved_error() {
+    public void disabled_testRequestPinShortcut_manifestExists_thenRemoved_error() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1344,7 +1344,7 @@
      * The manifest shortcut existed, but before accepting(), it's removed.  Because the request
      * has all the mandatory fields, we can go ahead and still publish it.
      */
-    public void testRequestPinShortcut_manifestExists_thenRemoved_okay() {
+    public void disabled_testRequestPinShortcut_manifestExists_thenRemoved_okay() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1404,7 +1404,7 @@
      * The dynamic shortcut existed, but before accepting(), it's removed.  Because the request
      * has a partial shortcut, accept() should fail.
      */
-    public void testRequestPinShortcut_dynamicExists_thenDisabled_error() {
+    public void disabled_testRequestPinShortcut_dynamicExists_thenDisabled_error() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1478,7 +1478,7 @@
      * The manifest shortcut existed, but before accepting(), it's removed.  Because the request
      * has a partial shortcut, accept() should fail.
      */
-    public void testRequestPinShortcut_manifestExists_thenDisabled_error() {
+    public void disabled_testRequestPinShortcut_manifestExists_thenDisabled_error() {
         setDefaultLauncher(USER_10, LAUNCHER_1);
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
index 263ada8..148c968 100644
--- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
@@ -69,7 +69,6 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.platform.test.annotations.EnableFlags;
 import android.service.quicksettings.TileService;
 import android.testing.TestableContext;
 
@@ -80,7 +79,6 @@
 import com.android.server.LocalServices;
 import com.android.server.policy.GlobalActionsProvider;
 import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.systemui.shared.Flags;
 
 import libcore.junit.util.compat.CoreCompatChangeRule;
 
@@ -107,7 +105,6 @@
             TEST_SERVICE);
     private static final CharSequence APP_NAME = "AppName";
     private static final CharSequence TILE_LABEL = "Tile label";
-    private static final int SECONDARY_DISPLAY_ID = 2;
 
     @Rule
     public final TestableContext mContext =
@@ -752,29 +749,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
-    public void testDisableForAllDisplays() throws Exception {
-        int user1Id = 0;
-        mockUidCheck();
-        mockCurrentUserCheck(user1Id);
-
-        mStatusBarManagerService.onDisplayAdded(SECONDARY_DISPLAY_ID);
-
-        int expectedFlags = DISABLE_MASK & DISABLE_BACK;
-        String pkg = mContext.getPackageName();
-
-        // before disabling
-        assertEquals(DISABLE_NONE,
-                mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
-
-        // disable
-        mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
-
-        verify(mMockStatusBar).disable(0, expectedFlags, 0);
-        verify(mMockStatusBar).disable(SECONDARY_DISPLAY_ID, expectedFlags, 0);
-    }
-
-    @Test
     public void testSetHomeDisabled() throws Exception {
         int expectedFlags = DISABLE_MASK & DISABLE_HOME;
         String pkg = mContext.getPackageName();
@@ -877,29 +851,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
-    public void testDisable2ForAllDisplays() throws Exception {
-        int user1Id = 0;
-        mockUidCheck();
-        mockCurrentUserCheck(user1Id);
-
-        mStatusBarManagerService.onDisplayAdded(SECONDARY_DISPLAY_ID);
-
-        int expectedFlags = DISABLE2_MASK & DISABLE2_NOTIFICATION_SHADE;
-        String pkg = mContext.getPackageName();
-
-        // before disabling
-        assertEquals(DISABLE_NONE,
-                mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
-
-        // disable
-        mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
-
-        verify(mMockStatusBar).disable(0, 0, expectedFlags);
-        verify(mMockStatusBar).disable(SECONDARY_DISPLAY_ID, 0, expectedFlags);
-    }
-
-    @Test
     public void testSetQuickSettingsDisabled2() throws Exception {
         int expectedFlags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS;
         String pkg = mContext.getPackageName();
@@ -1141,7 +1092,6 @@
         // disable
         mStatusBarManagerService.disableForUser(expectedUser1Flags, mMockStatusBar, pkg, user1Id);
         mStatusBarManagerService.disableForUser(expectedUser2Flags, mMockStatusBar, pkg, user2Id);
-
         // check that right flag is disabled
         assertEquals(expectedUser1Flags,
                 mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index 5862ac6..b150b14 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -92,6 +92,21 @@
         simulateUserStarting(USER_ID)
 
         assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+        assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+            .isEqualTo(systemSupervisionPackage)
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+    fun onUserStarting_legacyProfileOwnerComponent_enablesSupervision() {
+        whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+            .thenReturn(supervisionProfileOwnerComponent)
+
+        simulateUserStarting(USER_ID)
+
+        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+        assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+            .isEqualTo(supervisionProfileOwnerComponent.packageName)
     }
 
     @Test
@@ -103,6 +118,7 @@
         simulateUserStarting(USER_ID, preCreated = true)
 
         assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+        assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
     }
 
     @Test
@@ -114,6 +130,7 @@
         simulateUserStarting(USER_ID)
 
         assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+        assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
     }
 
     @Test
@@ -125,6 +142,21 @@
         broadcastProfileOwnerChanged(USER_ID)
 
         assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+        assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+            .isEqualTo(systemSupervisionPackage)
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+    fun profileOwnerChanged_legacyProfileOwnerComponent_enablesSupervision() {
+        whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+            .thenReturn(supervisionProfileOwnerComponent)
+
+        broadcastProfileOwnerChanged(USER_ID)
+
+        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+        assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+            .isEqualTo(supervisionProfileOwnerComponent.packageName)
     }
 
     @Test
@@ -136,6 +168,7 @@
         broadcastProfileOwnerChanged(USER_ID)
 
         assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+        assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
     }
 
     @Test
@@ -175,7 +208,7 @@
     }
 
     @Test
-    fun supervisionEnabledForUser_internal() {
+    fun setSupervisionEnabledForUser_internal() {
         assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
 
         service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
@@ -205,6 +238,13 @@
     private val systemSupervisionPackage: String
         get() = context.getResources().getString(R.string.config_systemSupervision)
 
+    private val supervisionProfileOwnerComponent: ComponentName
+        get() =
+            context
+                .getResources()
+                .getString(R.string.config_defaultSupervisionProfileOwnerComponent)
+                .let(ComponentName::unflattenFromString)!!
+
     private fun simulateUserStarting(userId: Int, preCreated: Boolean = false) {
         val userInfo = UserInfo(userId, /* name= */ "tempUser", /* flags= */ 0)
         userInfo.preCreated = preCreated
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 842c441..857a976 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -452,14 +452,14 @@
                         + "    <library \n"
                         + "        name=\"foo\"\n"
                         + "        file=\"" + mFooJar + "\"\n"
-                        + "        on-bootclasspath-before=\"Q\"\n"
+                        + "        on-bootclasspath-before=\"A\"\n"
                         + "        on-bootclasspath-since=\"W\"\n"
                         + "     />\n\n"
                         + " </permissions>";
         parseSharedLibraries(contents);
         assertFooIsOnlySharedLibrary();
         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
-        assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
+        assertThat(entry.onBootclasspathBefore).isEqualTo("A");
         assertThat(entry.onBootclasspathSince).isEqualTo("W");
     }
 
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index a63a38d..0eb20eb 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -20,6 +20,7 @@
     ],
 
     static_libs: [
+        "compatibility-device-util-axt-minus-dexmaker",
         "frameworks-base-testutils",
         "services.accessibility",
         "services.core",
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index 4315254..69f1775 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -45,6 +45,8 @@
         <provider android:name=".DummyProvider"
             android:authorities="com.android.services.uitests" />
 
+        <activity android:name="android.app.ExampleActivity" />
+
     </application>
 
     <instrumentation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/services/tests/uiservicestests/src/android/app/ExampleActivity.java
similarity index 64%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
copy to services/tests/uiservicestests/src/android/app/ExampleActivity.java
index aa262f9..58395e4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/services/tests/uiservicestests/src/android/app/ExampleActivity.java
@@ -14,14 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.utils
+package android.app;
 
-import android.app.Instrumentation
-
-object RecentTasksUtils {
-    fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
-        instrumentation.uiAutomation.executeShellCommand(
-            "dumpsys activity service SystemUIService WMShell recents clearAll"
-        )
-    }
-}
\ No newline at end of file
+public class ExampleActivity extends Activity {
+}
diff --git a/services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java b/services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java
new file mode 100644
index 0000000..779fa1a
--- /dev/null
+++ b/services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2025 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 android.app;
+
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationSystemUtil.runAsSystemUi;
+import static android.app.NotificationSystemUtil.toggleNotificationPolicyAccess;
+import static android.service.notification.Condition.STATE_FALSE;
+import static android.service.notification.Condition.STATE_TRUE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.service.notification.Condition;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+public class NotificationManagerZenTest {
+
+    private Context mContext;
+    private NotificationManager mNotificationManager;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = ApplicationProvider.getApplicationContext();
+        mNotificationManager = mContext.getSystemService(NotificationManager.class);
+
+        toggleNotificationPolicyAccess(mContext, mContext.getPackageName(), true);
+        runAsSystemUi(() -> mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL));
+        removeAutomaticZenRules();
+    }
+
+    @After
+    public void tearDown() {
+        runAsSystemUi(() -> mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL));
+        removeAutomaticZenRules();
+    }
+
+    private void removeAutomaticZenRules() {
+        // Delete AZRs created by this test (query "as app", then delete "as system" so they are
+        // not preserved to be restored later).
+        Map<String, AutomaticZenRule> rules = mNotificationManager.getAutomaticZenRules();
+        runAsSystemUi(() -> {
+            for (String ruleId : rules.keySet()) {
+                mNotificationManager.removeAutomaticZenRule(ruleId);
+            }
+        });
+    }
+
+    @Test
+    @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_manualActivation() {
+        AutomaticZenRule ruleToCreate = createZenRule("rule");
+        String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+        Condition manualActivate = new Condition(ruleToCreate.getConditionId(), "manual-on",
+                STATE_TRUE, Condition.SOURCE_USER_ACTION);
+        Condition manualDeactivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+                STATE_FALSE, Condition.SOURCE_USER_ACTION);
+        Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+                STATE_TRUE);
+        Condition autoDeactivate = new Condition(ruleToCreate.getConditionId(), "auto-off",
+                STATE_FALSE);
+
+        // User manually activates -> it's active.
+        runAsSystemUi(
+                () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualActivate));
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+        // User manually deactivates -> it's inactive.
+        runAsSystemUi(
+                () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualDeactivate));
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+        // And app can activate and deactivate.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_manualDeactivation() {
+        AutomaticZenRule ruleToCreate = createZenRule("rule");
+        String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+        Condition manualActivate = new Condition(ruleToCreate.getConditionId(), "manual-on",
+                STATE_TRUE, Condition.SOURCE_USER_ACTION);
+        Condition manualDeactivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+                STATE_FALSE, Condition.SOURCE_USER_ACTION);
+        Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+                STATE_TRUE);
+        Condition autoDeactivate = new Condition(ruleToCreate.getConditionId(), "auto-off",
+                STATE_FALSE);
+
+        // App activates rule.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+        // User manually deactivates -> it's inactive.
+        runAsSystemUi(
+                () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualDeactivate));
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+        // User manually reactivates -> it's active.
+        runAsSystemUi(
+                () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualActivate));
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+        // That manual activation removed the override-deactivate, but didn't put an
+        // override-activate, so app can deactivate when its natural schedule ends.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_respectsManuallyActivated() {
+        AutomaticZenRule ruleToCreate = createZenRule("rule");
+        String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+        Condition manualActivate = new Condition(ruleToCreate.getConditionId(), "manual-on",
+                STATE_TRUE, Condition.SOURCE_USER_ACTION);
+        Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+                STATE_TRUE);
+        Condition autoDeactivate = new Condition(ruleToCreate.getConditionId(), "auto-off",
+                STATE_FALSE);
+
+        // App thinks rule should be inactive.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+        // Manually activate -> it's active.
+        runAsSystemUi(() -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualActivate));
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+        // App says it should be inactive, but it's ignored.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+        // App says it should be active. No change now...
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+        // ... but when the app wants to deactivate next time, it works.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_respectsManuallyDeactivated() {
+        AutomaticZenRule ruleToCreate = createZenRule("rule");
+        String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+        Condition manualDeactivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+                STATE_FALSE, Condition.SOURCE_USER_ACTION);
+        Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+                STATE_TRUE);
+        Condition autoDeactivate = new Condition(ruleToCreate.getConditionId(), "auto-off",
+                STATE_FALSE);
+
+        // App activates rule.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+        // User manually deactivates -> it's inactive.
+        runAsSystemUi(
+                () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualDeactivate));
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+        // App says it should be active, but it's ignored.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+        // App says it should be inactive. No change now...
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+        // ... but when the app wants to activate next time, it works.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_manualActivationFromApp() {
+        AutomaticZenRule ruleToCreate = createZenRule("rule");
+        String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+        Condition manualActivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+                STATE_TRUE, Condition.SOURCE_USER_ACTION);
+        Condition manualDeactivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+                STATE_FALSE, Condition.SOURCE_USER_ACTION);
+        Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+                STATE_TRUE);
+        Condition autoDeactivate = new Condition(ruleToCreate.getConditionId(), "auto-off",
+                STATE_FALSE);
+
+        // App activates rule.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+        // User manually deactivates from SysUI -> it's inactive.
+        runAsSystemUi(
+                () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualDeactivate));
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+        // User manually activates from App -> it's active.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, manualActivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+        // And app can automatically deactivate it later.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_manualDeactivationFromApp() {
+        AutomaticZenRule ruleToCreate = createZenRule("rule");
+        String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+        Condition manualActivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+                STATE_TRUE, Condition.SOURCE_USER_ACTION);
+        Condition manualDeactivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+                STATE_FALSE, Condition.SOURCE_USER_ACTION);
+        Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+                STATE_TRUE);
+
+        // User manually activates from SysUI -> it's active.
+        runAsSystemUi(
+                () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualActivate));
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+        // User manually deactivates from App -> it's inactive.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, manualDeactivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+        // And app can automatically activate it later.
+        mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+        assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+    }
+
+    private AutomaticZenRule createZenRule(String name) {
+        return createZenRule(name, NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+    }
+
+    private AutomaticZenRule createZenRule(String name, int filter) {
+        return new AutomaticZenRule(name, null,
+                new ComponentName(mContext, ExampleActivity.class),
+                new Uri.Builder().scheme("scheme")
+                        .appendPath("path")
+                        .appendQueryParameter("fake_rule", "fake_value")
+                        .build(), null, filter, true);
+    }
+}
diff --git a/services/tests/uiservicestests/src/android/app/NotificationSystemUtil.java b/services/tests/uiservicestests/src/android/app/NotificationSystemUtil.java
new file mode 100644
index 0000000..cf6e39b
--- /dev/null
+++ b/services/tests/uiservicestests/src/android/app/NotificationSystemUtil.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025 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 android.app;
+
+import static org.junit.Assert.assertEquals;
+
+import android.Manifest;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.AmUtils;
+import com.android.compatibility.common.util.FileUtils;
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.ThrowingRunnable;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+public class NotificationSystemUtil {
+
+    /**
+     * Runs a {@link ThrowingRunnable} as the Shell, while adopting SystemUI's permission (as
+     * checked by {@code NotificationManagerService#isCallerSystemOrSystemUi}).
+     */
+    protected static void runAsSystemUi(@NonNull ThrowingRunnable runnable) {
+        SystemUtil.runWithShellPermissionIdentity(
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                runnable, Manifest.permission.STATUS_BAR_SERVICE);
+    }
+
+    static void toggleNotificationPolicyAccess(Context context, String packageName,
+            boolean on) throws IOException {
+
+        String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName
+                + " " + context.getUserId();
+
+        runCommand(command, InstrumentationRegistry.getInstrumentation());
+        AmUtils.waitForBroadcastBarrier();
+
+        NotificationManager nm = context.getSystemService(NotificationManager.class);
+        assertEquals("Notification Policy Access Grant is "
+                + nm.isNotificationPolicyAccessGranted() + " not " + on + " for "
+                + packageName, on, nm.isNotificationPolicyAccessGranted());
+    }
+
+    private static void runCommand(String command, Instrumentation instrumentation)
+            throws IOException {
+        UiAutomation uiAutomation = instrumentation.getUiAutomation();
+        try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(
+                uiAutomation.executeShellCommand(command))) {
+            FileUtils.readInputStreamFully(fis);
+        }
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index d1afa38..19b90b6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -17,6 +17,7 @@
 
 import static android.os.UserHandle.USER_ALL;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
 import static android.service.notification.Adjustment.KEY_TYPE;
 import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
 import static android.service.notification.Adjustment.TYPE_NEWS;
@@ -45,6 +46,7 @@
 
 import android.Manifest;
 import android.app.ActivityManager;
+import android.app.Flags;
 import android.app.INotificationManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -677,7 +679,7 @@
     }
 
     @Test
-    @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
     public void testDisallowAdjustmentType_readWriteXml_entries() throws Exception {
         int userId = ActivityManager.getCurrentUser();
 
@@ -705,12 +707,12 @@
     @Test
     @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
     public void testSetAssistantAdjustmentKeyTypeState_allow() {
-        assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+        assertThat(mAssistants.getAllowedClassificationTypes()).asList()
                 .containsExactly(TYPE_PROMOTION);
 
         mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
 
-        assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+        assertThat(mAssistants.getAllowedClassificationTypes()).asList()
                 .containsExactlyElementsIn(List.of(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION));
     }
 
@@ -718,11 +720,11 @@
     @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
     public void testSetAssistantAdjustmentKeyTypeState_disallow() {
         mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
-        assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).isEmpty();
+        assertThat(mAssistants.getAllowedClassificationTypes()).isEmpty();
     }
 
     @Test
-    @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
     public void testDisallowAdjustmentKeyType_readWriteXml() throws Exception {
         mAssistants.loadDefaultsFromConfig(true);
         mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
@@ -731,7 +733,7 @@
 
         writeXmlAndReload(USER_ALL);
 
-        assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+        assertThat(mAssistants.getAllowedClassificationTypes()).asList()
                 .containsExactlyElementsIn(List.of(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION));
     }
 
@@ -742,105 +744,105 @@
 
         writeXmlAndReload(USER_ALL);
 
-        assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+        assertThat(mAssistants.getAllowedClassificationTypes()).asList()
                 .containsExactly(TYPE_PROMOTION);
     }
 
     @Test
-    @EnableFlags({android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
-            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
-    public void testGetTypeAdjustmentDeniedPackages() throws Exception {
-        String pkg = "my.package";
-        String pkg2 = "my.package.2";
-        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg)).isTrue();
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
-
-        mAssistants.setTypeAdjustmentForPackageState(pkg, true);
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
-        mAssistants.setTypeAdjustmentForPackageState(pkg, false);
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactly(pkg);
-        mAssistants.setTypeAdjustmentForPackageState(pkg2, true);
-      assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactly(pkg);
-        mAssistants.setTypeAdjustmentForPackageState(pkg2, false);
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactly(pkg, pkg2);
-    }
-
-    @Test
-    @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-    public void testSetTypeAdjustmentForPackageState_allowsAndDenies() {
-        // Given that a package is allowed to have its type adjusted,
+    @EnableFlags({Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI, Flags.FLAG_NM_SUMMARIZATION,
+            Flags.FLAG_NM_SUMMARIZATION_UI})
+    public void testSetAdjustmentSupportedForPackage_allowsAndDenies() {
+        // Given that a package is allowed to have summarization adjustments
+        String key = KEY_SUMMARIZATION;
         String allowedPackage = "allowed.package";
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
-        mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true);
 
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
-        assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, allowedPackage)).isTrue();
 
         // Set type adjustment disallowed for this package
-        mAssistants.setTypeAdjustmentForPackageState(allowedPackage, false);
+        mAssistants.setAdjustmentSupportedForPackage(key, allowedPackage, false);
 
         // Then the package is marked as denied
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactly(allowedPackage);
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, allowedPackage)).isFalse();
 
         // Set type adjustment allowed again
-        mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true);
+        mAssistants.setAdjustmentSupportedForPackage(key, allowedPackage, true);
 
         // Then the package is marked as allowed again
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
-        assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, allowedPackage)).isTrue();
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-    public void testSetAssistantAdjustmentKeyTypeStateForPackage_deniesMultiple() {
-        // Given packages not allowed to have their type adjusted,
+    @EnableFlags({Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI, Flags.FLAG_NM_SUMMARIZATION,
+            Flags.FLAG_NM_SUMMARIZATION_UI})
+    public void testSetAdjustmentSupportedForPackage_deniesMultiple() {
+        // Given packages not allowed to have summarizations applied
+        String key = KEY_SUMMARIZATION;
         String deniedPkg1 = "denied.Pkg1";
         String deniedPkg2 = "denied.Pkg2";
         String deniedPkg3 = "denied.Pkg3";
         // Set type adjustment disallowed for these packages
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg1, false);
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, false);
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg3, false);
+        mAssistants.setAdjustmentSupportedForPackage(key, deniedPkg1, false);
+        mAssistants.setAdjustmentSupportedForPackage(key, deniedPkg2, false);
+        mAssistants.setAdjustmentSupportedForPackage(key, deniedPkg3, false);
 
         // Then the packages are marked as denied
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg2, deniedPkg3));
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg1));
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg2));
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg3));
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, deniedPkg1)).isFalse();
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, deniedPkg2)).isFalse();
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, deniedPkg3)).isFalse();
 
         // And when we re-allow one of them,
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, true);
+        mAssistants.setAdjustmentSupportedForPackage(key, deniedPkg2, true);
 
         // Then the rest of the original packages are still marked as denied.
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg3));
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg1));
-        assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg2));
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg3));
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, deniedPkg1)).isFalse();
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, deniedPkg2)).isTrue();
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, deniedPkg3)).isFalse();
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-    public void testSetAssistantAdjustmentKeyTypeStateForPackage_readWriteXml() throws Exception {
+    @EnableFlags({Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI, Flags.FLAG_NM_SUMMARIZATION,
+            Flags.FLAG_NM_SUMMARIZATION_UI})
+    public void testSetAdjustmentSupportedForPackage_readWriteXml_singleAdjustment()
+            throws Exception {
         mAssistants.loadDefaultsFromConfig(true);
+        String key = KEY_SUMMARIZATION;
         String deniedPkg1 = "denied.Pkg1";
         String allowedPkg2 = "allowed.Pkg2";
         String deniedPkg3 = "denied.Pkg3";
-        // Set type adjustment disallowed or allowed for these packages
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg1, false);
-        mAssistants.setTypeAdjustmentForPackageState(allowedPkg2, true);
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg3, false);
+        // Set summarization adjustment disallowed or allowed for these packages
+        mAssistants.setAdjustmentSupportedForPackage(key, deniedPkg1, false);
+        mAssistants.setAdjustmentSupportedForPackage(key, allowedPkg2, true);
+        mAssistants.setAdjustmentSupportedForPackage(key, deniedPkg3, false);
 
         writeXmlAndReload(USER_ALL);
 
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg3));
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, deniedPkg1)).isFalse();
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, allowedPkg2)).isTrue();
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(key, deniedPkg3)).isFalse();
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI, Flags.FLAG_NM_SUMMARIZATION,
+            Flags.FLAG_NM_SUMMARIZATION_UI})
+    public void testSetAdjustmentSupportedForPackage_readWriteXml_multipleAdjustments()
+            throws Exception {
+        mAssistants.loadDefaultsFromConfig(true);
+        String deniedPkg1 = "denied.Pkg1";
+        String deniedPkg2 = "denied.Pkg2";
+        String deniedPkg3 = "denied.Pkg3";
+        // Set summarization adjustment disallowed these packages
+        mAssistants.setAdjustmentSupportedForPackage(KEY_SUMMARIZATION, deniedPkg1, false);
+        mAssistants.setAdjustmentSupportedForPackage(KEY_SUMMARIZATION, deniedPkg3, false);
+        // Set classification adjustment disallowed for these packages
+        mAssistants.setAdjustmentSupportedForPackage(KEY_TYPE, deniedPkg2, false);
+
+        writeXmlAndReload(USER_ALL);
+
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(KEY_SUMMARIZATION, deniedPkg1))
+                .isFalse();
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(KEY_TYPE, deniedPkg2)).isFalse();
+        assertThat(mAssistants.isAdjustmentAllowedForPackage(KEY_SUMMARIZATION, deniedPkg3))
+                .isFalse();
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 8fad01a..2616ccb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -247,7 +247,8 @@
                     getRankingAdjustment(i),
                     isBubble(i),
                     getProposedImportance(i),
-                    hasSensitiveContent(i)
+                    hasSensitiveContent(i),
+                    getSummarization(i)
             );
             rankings[i] = ranking;
         }
@@ -383,6 +384,13 @@
         return index % 3 == 0;
     }
 
+    public static String getSummarization(int index) {
+        if ((android.app.Flags.nmSummarizationUi() || android.app.Flags.nmSummarization())) {
+            return "summary " + index;
+        }
+        return null;
+    }
+
     private boolean isBubble(int index) {
         return index % 4 == 0;
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index edfdb4f..f8c8a1d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -24,6 +24,7 @@
 import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
+import static android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS;
 import static android.app.Flags.FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN;
 import static android.app.Flags.FLAG_SORT_SECTION_BY_TIME;
 import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP;
@@ -51,6 +52,7 @@
 import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
 import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
 import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
+import static android.app.NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED;
 import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
@@ -123,7 +125,9 @@
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_LOCKDOWN;
@@ -163,6 +167,7 @@
 
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyLong;
@@ -258,6 +263,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Parcel;
@@ -360,7 +366,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
-import org.mockito.ArgumentMatchers;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -368,6 +373,9 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -383,9 +391,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Consumer;
 
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4.class)
 @RunWithLooper
@@ -574,6 +579,7 @@
     NetworkCapabilities mWifiNetworkCapabilities;
 
     private NotificationManagerService.WorkerHandler mWorkerHandler;
+    private Handler mBroadcastsHandler;
 
     private class TestableToastCallback extends ITransientNotification.Stub {
         @Override
@@ -810,18 +816,25 @@
         when(mUmInternal.isUserInitialized(anyInt())).thenReturn(true);
 
         mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
-        mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
-                mLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
-                mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm,
-                mAppUsageStats, mDevicePolicyManager, mUgm, mUgmInternal,
-                mAppOpsManager, mUm, mHistoryManager, mStatsManager,
-                mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class),
-                mTelecomManager, mLogger, mTestFlagResolver, mPermissionManager,
-                mPowerManager, mConnectivityManager, mPostNotificationTrackerFactory);
+        mBroadcastsHandler = new Handler(mTestableLooper.getLooper());
+
+        mService.init(mWorkerHandler, mRankingHandler, mBroadcastsHandler, mPackageManager,
+                mPackageManagerClient, mLightsManager, mListeners, mAssistants, mConditionProviders,
+                mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
+                mGroupHelper, mAm, mAtm, mAppUsageStats, mDevicePolicyManager, mUgm, mUgmInternal,
+                mAppOpsManager, mUm, mHistoryManager, mStatsManager, mAmi, mToastRateLimiter,
+                mPermissionHelper, mock(UsageStatsManagerInternal.class), mTelecomManager, mLogger,
+                mTestFlagResolver, mPermissionManager, mPowerManager, mConnectivityManager,
+                mPostNotificationTrackerFactory);
 
         mService.setAttentionHelper(mAttentionHelper);
         mService.setLockPatternUtils(mock(LockPatternUtils.class));
 
+        // make sure PreferencesHelper doesn't try to interact with any real caches
+        PreferencesHelper prefHelper = spy(mService.mPreferencesHelper);
+        doNothing().when(prefHelper).invalidateNotificationChannelCache();
+        mService.setPreferencesHelper(prefHelper);
+
         // Return first true for RoleObserver main-thread check
         when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
         ModuleInfo moduleInfo = new ModuleInfo();
@@ -994,6 +1007,9 @@
             // problematic interactions with mocks when they're no longer working as expected).
             mWorkerHandler.removeCallbacksAndMessages(null);
         }
+        if (mBroadcastsHandler != null) {
+            mBroadcastsHandler.removeCallbacksAndMessages(null);
+        }
 
         if (mTestableLooper != null) {
             // Must remove static reference to this test object to prevent leak (b/261039202)
@@ -7673,7 +7689,7 @@
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
 
         // Set up notifications that will be adjusted
         final NotificationRecord r1 = spy(generateNotificationRecord(
@@ -10889,6 +10905,7 @@
         Bundle signals = new Bundle();
         signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
         signals.putInt(KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
+        signals.putInt(KEY_TYPE, TYPE_PROMOTION);
         Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
                "", r.getUser().getIdentifier());
 
@@ -11413,8 +11430,14 @@
         verify(mContext).sendBroadcastAsUser(eqIntent(expected), eq(UserHandle.of(mUserId)));
     }
 
+    private static Intent isIntentWithAction(String wantedAction) {
+        return argThat(
+                intent -> intent != null && wantedAction.equals(intent.getAction())
+        );
+    }
+
     private static Intent eqIntent(Intent wanted) {
-        return ArgumentMatchers.argThat(
+        return argThat(
                 new ArgumentMatcher<Intent>() {
                     @Override
                     public boolean matches(Intent argument) {
@@ -11996,7 +12019,8 @@
     @Test
     @DisableFlags(android.app.Flags.FLAG_REMOVE_REMOTE_VIEWS)
     public void testRemoveLargeRemoteViews() throws Exception {
-        int removeSize = mContext.getResources().getInteger(
+        // Cast to long to mock RemoteViews.estimateMemoryUsage which returns long.
+        long removeSize = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_notificationStripRemoteViewSizeBytes);
 
         RemoteViews rv = mock(RemoteViews.class);
@@ -17491,6 +17515,66 @@
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_NM_BINDER_PERF_THROTTLE_EFFECTS_SUPPRESSOR_BROADCAST,
+            Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS})
+    public void requestHintsFromListener_changingEffectsButNotSuppressor_noBroadcast()
+            throws Exception {
+        // Note that NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS is not strictly necessary; however each
+        // path will do slightly different calls so we force one of them to simplify the test.
+        when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+        INotificationListener token = mock(INotificationListener.class);
+        mService.isSystemUid = true;
+
+        mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+        mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+        waitForIdle();
+
+        verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+                isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+
+        // Same suppressor suppresses something else.
+        mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+        mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+        waitForIdle();
+
+        // Still 1 total calls (the previous one).
+        verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+                isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_NM_BINDER_PERF_THROTTLE_EFFECTS_SUPPRESSOR_BROADCAST,
+            Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS})
+    public void requestHintsFromListener_changingSuppressor_throttlesBroadcast() throws Exception {
+        // Note that NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS is not strictly necessary; however each
+        // path will do slightly different calls so we force one of them to simplify the test.
+        when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+        INotificationListener token = mock(INotificationListener.class);
+        mService.isSystemUid = true;
+
+        // Several updates in quick succession.
+        mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+        mBinderService.clearRequestedListenerHints(token);
+        mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+        mBinderService.clearRequestedListenerHints(token);
+        mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+        mBinderService.clearRequestedListenerHints(token);
+        mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+
+        // No broadcasts yet!
+        verify(mContext, never()).sendBroadcastMultiplePermissions(any(), any(), any(), any());
+
+        mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+        waitForIdle();
+
+        // Only one broadcast after idle time.
+        verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+                isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+    }
+
+    @Test
     @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
     public void testApplyAdjustment_keyType_validType() throws Exception {
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
@@ -17499,7 +17583,7 @@
                 NotificationManagerService.WorkerHandler.class);
         mService.setHandler(handler);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
 
         Bundle signals = new Bundle();
         signals.putInt(KEY_TYPE, TYPE_NEWS);
@@ -17543,7 +17627,7 @@
                 NotificationManagerService.WorkerHandler.class);
         mService.setHandler(handler);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
 
         Bundle signals = new Bundle();
         signals.putInt(KEY_TYPE, TYPE_NEWS);
@@ -17559,7 +17643,7 @@
         assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
 
         // When we block adjustments for this package
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(false);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(false);
 
         signals.putInt(KEY_TYPE, TYPE_PROMOTION);
         mBinderService.applyAdjustmentFromAssistant(null, adjustment);
@@ -17570,8 +17654,19 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
-    public void testSetCanBePromoted_granted() throws Exception {
+    @EnableFlags({android.app.Flags.FLAG_API_RICH_ONGOING})
+    public void testSetCanBePromoted_granted_noui() throws Exception {
+        testSetCanBePromoted_granted();
+    }
+
+    @Test
+    @EnableFlags({android.app.Flags.FLAG_API_RICH_ONGOING,
+            android.app.Flags.FLAG_UI_RICH_ONGOING })
+    public void testSetCanBePromoted_granted_ui() throws Exception {
+        testSetCanBePromoted_granted();
+    }
+
+    private void testSetCanBePromoted_granted() throws Exception {
         // qualifying posted notification
         Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17626,6 +17721,11 @@
         mService.addNotification(r);
         mService.addEnqueuedNotification(r1);
 
+        // GIVEN - make sure the promoted value does not depend on the default value.
+        mBinderService.setCanBePromoted(mPkg, mUid, false, true);
+        waitForIdle();
+        clearInvocations(mListeners);
+
         mBinderService.setCanBePromoted(mPkg, mUid, true, true);
 
         waitForIdle();
@@ -17648,7 +17748,18 @@
 
     @Test
     @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
-    public void testSetCanBePromoted_granted_onlyNotifiesOnce() throws Exception {
+    public void testSetCanBePromoted_granted_onlyNotifiesOnce_noui() throws Exception {
+        testSetCanBePromoted_granted_onlyNotifiesOnce();
+    }
+
+    @Test
+    @EnableFlags({android.app.Flags.FLAG_API_RICH_ONGOING,
+            android.app.Flags.FLAG_UI_RICH_ONGOING})
+    public void testSetCanBePromoted_granted_onlyNotifiesOnce_ui() throws Exception {
+        testSetCanBePromoted_granted_onlyNotifiesOnce();
+    }
+
+    private void testSetCanBePromoted_granted_onlyNotifiesOnce() throws Exception {
         // qualifying posted notification
         Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17664,6 +17775,10 @@
         NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         mService.addNotification(r);
+        // GIVEN - make sure the promoted value does not depend on the default value.
+        mBinderService.setCanBePromoted(mPkg, mUid, false, true);
+        waitForIdle();
+        clearInvocations(mListeners);
 
         mBinderService.setCanBePromoted(mPkg, mUid, true, true);
         waitForIdle();
@@ -17881,15 +17996,16 @@
     @Test
     @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
             FLAG_NOTIFICATION_FORCE_GROUPING,
-            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
-    public void testUnbundleNotification_ungrouped_restoresOriginalChannel() throws Exception {
+            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+    public void testUnclassifyNotification_ungrouped_restoresOriginalChannel() throws Exception {
         NotificationManagerService.WorkerHandler handler = mock(
                 NotificationManagerService.WorkerHandler.class);
         mService.setHandler(handler);
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
 
         // Post a single notification
         final boolean hasOriginalSummary = false;
@@ -17909,9 +18025,11 @@
         assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
         // Check that the Notification mChannelId is not updated
         assertThat(r.getNotification().getChannelId()).isEqualTo(TEST_CHANNEL_ID);
+        // Check that the bundleType is updated
+        assertThat(r.getBundleType()).isEqualTo(Adjustment.TYPE_NEWS);
 
-        // Unbundle the notification
-        mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+        // Unclassify the notification
+        mService.unclassifyNotification(keyToUnbundle);
 
         // Check that the original channel was restored
         assertThat(r.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
@@ -17921,15 +18039,16 @@
     @Test
     @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
             FLAG_NOTIFICATION_FORCE_GROUPING,
-            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
-    public void testUnbundleNotification_grouped_restoresOriginalChannel() throws Exception {
+            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+    public void testUnclassifyNotification_grouped_restoresOriginalChannel() throws Exception {
         NotificationManagerService.WorkerHandler handler = mock(
                 NotificationManagerService.WorkerHandler.class);
         mService.setHandler(handler);
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
 
         // Post grouped notifications
         final String originalGroupName = "originalGroup";
@@ -17957,9 +18076,10 @@
         waitForIdle();
         r1.applyAdjustments();
         assertThat(r1.getChannel().getId()).isEqualTo(NEWS_ID);
+        assertThat(r1.getBundleType()).isEqualTo(Adjustment.TYPE_NEWS);
 
-        // Unbundle the notification
-        mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+        // Unclassify the notification
+        mService.unclassifyNotification(keyToUnbundle);
 
         // Check that the original channel was restored
         assertThat(r1.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
@@ -17968,9 +18088,10 @@
 
     @Test
     @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
-        FLAG_NOTIFICATION_FORCE_GROUPING,
-        FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
-    public void testUnbundleNotification_groupedSummaryCanceled_restoresOriginalChannel()
+            FLAG_NOTIFICATION_FORCE_GROUPING,
+            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+    public void testUnclassifyNotification_groupedSummaryCanceled_restoresOriginalChannel()
             throws Exception {
         NotificationManagerService.WorkerHandler handler = mock(
                 NotificationManagerService.WorkerHandler.class);
@@ -17978,7 +18099,7 @@
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
 
         // Post grouped notifications
         final String originalGroupName = "originalGroup";
@@ -18005,13 +18126,14 @@
         waitForIdle();
         r1.applyAdjustments();
         assertThat(r1.getChannel().getId()).isEqualTo(NEWS_ID);
+        assertThat(r1.getBundleType()).isEqualTo(Adjustment.TYPE_NEWS);
 
         // Cancel original summary
         final boolean hasOriginalSummary = false;
         mService.mSummaryByGroupKey.remove(summary.getGroupKey());
 
-        // Unbundle the notification
-        mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+        // Unclassify the notification
+        mService.unclassifyNotification(keyToUnbundle);
 
         // Check that the original channel was restored
         assertThat(r1.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
@@ -18021,15 +18143,16 @@
     @Test
     @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
             FLAG_NOTIFICATION_FORCE_GROUPING,
-            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
-    public void testRebundleNotification_restoresBundleChannel() throws Exception {
+            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+    public void testReclassifyNotification_restoresBundleChannel() throws Exception {
         NotificationManagerService.WorkerHandler handler = mock(
                 NotificationManagerService.WorkerHandler.class);
         mService.setHandler(handler);
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
 
         // Post a single notification
         final boolean hasOriginalSummary = false;
@@ -18050,18 +18173,17 @@
         assertThat(r.getBundleType()).isEqualTo(Adjustment.TYPE_NEWS);
 
         // Unbundle the notification
-        mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+        mService.unclassifyNotification(keyToUnbundle);
 
         // Check that the original channel was restored
         assertThat(r.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
-        assertThat(r.getBundleType()).isEqualTo(Adjustment.TYPE_NEWS);
         verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(r), eq(hasOriginalSummary));
 
         Mockito.reset(mRankingHandler);
         Mockito.reset(mGroupHelper);
 
         // Rebundle the notification
-        mService.mNotificationDelegate.rebundleNotification(keyToUnbundle);
+        mService.reclassifyNotification(keyToUnbundle);
 
         // Actually apply the adjustments
         doAnswer(invocationOnMock -> {
@@ -18077,4 +18199,405 @@
         assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
     }
 
+    @Test
+    @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+            FLAG_NOTIFICATION_FORCE_GROUPING,
+            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+    public void testDisallowTypeAdj_unclassifiesAllNotifications() throws Exception {
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+        when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
+
+        // Post some notifications and classify in different bundles
+        final int numNotifications = NotificationChannel.SYSTEM_RESERVED_IDS.size();
+        for (int i = 0; i < numNotifications; i++) {
+            NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, i, mUserId);
+            mService.addNotification(r);
+            Bundle signals = new Bundle();
+            final int adjustmentType = i + 1;
+            signals.putInt(Adjustment.KEY_TYPE, adjustmentType);
+            Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
+                    "", r.getUser().getIdentifier());
+            mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+            waitForIdle();
+            r.applyAdjustments();
+            r.setBundleType(adjustmentType);
+            // Check that the NotificationRecord channel is updated
+            assertThat(r.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+            assertThat(r.getBundleType()).isEqualTo(adjustmentType);
+        }
+
+        // Disallow KEY_TYPE adjustment
+        mBinderService.disallowAssistantAdjustment(Adjustment.KEY_TYPE);
+        waitForIdle();
+
+        //Check that all notifications have been unbundled
+        for (NotificationRecord record : mService.mNotificationList) {
+            // Check that the original channel was restored
+            assertThat(record.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+            verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(record), anyBoolean());
+        }
+
+        // Re-allow KEY_TYPE adjustment
+        Mockito.reset(mRankingHandler);
+        Mockito.reset(mGroupHelper);
+        mBinderService.allowAssistantAdjustment(Adjustment.KEY_TYPE);
+        waitForIdle();
+
+        // Actually apply the adjustments
+        doAnswer(invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).applyAdjustments();
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).calculateImportance();
+            return null;
+        }).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
+        mService.handleRankingSort();
+
+        // Check that the bundle channel was restored for all notifications
+        verify(handler, times(numNotifications)).scheduleSendRankingUpdate();
+        verify(mRankingHandler, times(numNotifications)).requestSort();
+        for (NotificationRecord record : mService.mNotificationList) {
+            assertThat(record.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+        }
+    }
+
+    @Test
+    @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+            FLAG_NOTIFICATION_FORCE_GROUPING,
+            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+    public void testDisableBundleAdjustmentByType_unclassifiesNotifications() throws Exception {
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+        when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
+
+        // Post some notifications and classify in different bundles
+        final int numNotifications = NotificationChannel.SYSTEM_RESERVED_IDS.size();
+        final int numNewsNotifications = 1;
+        for (int i = 0; i < numNotifications; i++) {
+            NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, i, mUserId);
+            mService.addNotification(r);
+            Bundle signals = new Bundle();
+            final int adjustmentType = i + 1;
+            signals.putInt(Adjustment.KEY_TYPE, adjustmentType);
+            Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
+                    "", r.getUser().getIdentifier());
+            mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+            waitForIdle();
+            r.applyAdjustments();
+            r.setBundleType(adjustmentType);
+            // Check that the NotificationRecord channel is updated
+            assertThat(r.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+            assertThat(r.getBundleType()).isEqualTo(adjustmentType);
+        }
+
+        // Disable TYPE_NEWS bundle
+        mBinderService.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, false);
+        waitForIdle();
+
+        //Check that all notifications classified as TYPE_NEWS have been unbundled
+        for (NotificationRecord record : mService.mNotificationList) {
+            // Check that the original channel was restored
+            // for notifications classified as TYPE_NEWS
+            if (record.getBundleType() == TYPE_NEWS) {
+                assertThat(record.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+                verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(record), anyBoolean());
+            }
+        }
+
+        // Re-enable TYPE_NEWS bundle
+        Mockito.reset(mRankingHandler);
+        Mockito.reset(mGroupHelper);
+        mBinderService.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true);
+        waitForIdle();
+
+        // Actually apply the adjustments
+        doAnswer(invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).applyAdjustments();
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).calculateImportance();
+            return null;
+        }).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
+        mService.handleRankingSort();
+
+        // Check that the bundle channel was restored
+        verify(mRankingHandler, times(numNewsNotifications)).requestSort();
+        for (NotificationRecord record : mService.mNotificationList) {
+            assertThat(record.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+        }
+    }
+
+    @Test
+    @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+            FLAG_NOTIFICATION_FORCE_GROUPING,
+            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+    public void testDisableBundleAdjustmentByPkg_unclassifiesNotifications() throws Exception {
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+        when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
+
+        // Post some notifications and classify in different bundles
+        final int numNotifications = NotificationChannel.SYSTEM_RESERVED_IDS.size();
+        for (int i = 0; i < numNotifications; i++) {
+            NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, i, mUserId);
+            mService.addNotification(r);
+            Bundle signals = new Bundle();
+            final int adjustmentType = i + 1;
+            signals.putInt(Adjustment.KEY_TYPE, adjustmentType);
+            Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
+                    "", r.getUser().getIdentifier());
+            mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+            waitForIdle();
+            r.applyAdjustments();
+            r.setBundleType(adjustmentType);
+            // Check that the NotificationRecord channel is updated
+            assertThat(r.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+            assertThat(r.getBundleType()).isEqualTo(adjustmentType);
+        }
+
+        // Disable TYPE_NEWS bundle
+        mBinderService.setAdjustmentSupportedForPackage(KEY_TYPE, mPkg, false);
+        waitForIdle();
+
+        //Check that all notifications were unbundled
+        for (NotificationRecord record : mService.mNotificationList) {
+            assertThat(record.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+            verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(record), anyBoolean());
+        }
+
+        // Re-enable bundles for package
+        Mockito.reset(mRankingHandler);
+        Mockito.reset(mGroupHelper);
+        mBinderService.setAdjustmentSupportedForPackage(KEY_TYPE, mPkg, true);
+        waitForIdle();
+
+        // Actually apply the adjustments
+        doAnswer(invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).applyAdjustments();
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).calculateImportance();
+            return null;
+        }).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
+        mService.handleRankingSort();
+
+        // Check that the bundle channel was restored
+        verify(mRankingHandler, times(numNotifications)).requestSort();
+        for (NotificationRecord record : mService.mNotificationList) {
+            assertThat(record.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+        }
+    }
+
+    @Test
+    @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+            FLAG_NOTIFICATION_FORCE_GROUPING,
+            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+    public void testDisableBundleAdjustmentByPkg_unclassifiesEnqueuedNotifications()
+            throws Exception {
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+        when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
+
+        // Enqueue some notifications and classify in different bundles
+        final int numNotifications = NotificationChannel.SYSTEM_RESERVED_IDS.size();
+        for (int i = 0; i < numNotifications; i++) {
+            NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, i, mUserId);
+            mService.addEnqueuedNotification(r);
+            Bundle signals = new Bundle();
+            final int adjustmentType = i + 1;
+            signals.putInt(Adjustment.KEY_TYPE, adjustmentType);
+            Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
+                    "", r.getUser().getIdentifier());
+            mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+            waitForIdle();
+            r.applyAdjustments();
+            r.setBundleType(adjustmentType);
+            // Check that the NotificationRecord channel is updated
+            assertThat(r.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+            assertThat(r.getBundleType()).isEqualTo(adjustmentType);
+        }
+
+        // Disable type adjustment for the package
+        mBinderService.setAdjustmentSupportedForPackage(KEY_TYPE, mPkg, false);
+        waitForIdle();
+
+        //Check that all notifications were unbundled
+        for (NotificationRecord record : mService.mEnqueuedNotifications) {
+            assertThat(record.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+            verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(record), anyBoolean());
+        }
+
+        // Re-enable bundles for package
+        Mockito.reset(mRankingHandler);
+        Mockito.reset(mGroupHelper);
+        mBinderService.setAdjustmentSupportedForPackage(KEY_TYPE, mPkg, true);
+        waitForIdle();
+
+        // Actually apply the adjustments
+        doAnswer(invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).applyAdjustments();
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).calculateImportance();
+            return null;
+        }).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
+        mService.handleRankingSort();
+
+        // Check that the bundle channel was restored
+        verify(mRankingHandler, times(numNotifications)).requestSort();
+        for (NotificationRecord record : mService.mEnqueuedNotifications) {
+            record.applyAdjustments();
+            assertThat(record.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+        }
+    }
+
+    @Test
+    @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+            FLAG_NOTIFICATION_FORCE_GROUPING,
+            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+    public void testDisableBundleAdjustmentByType_unclassifiesEnqueuedNotifications()
+            throws Exception {
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+        when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
+
+        // Enqueue some notifications and classify in different bundles
+        final int numNotifications = NotificationChannel.SYSTEM_RESERVED_IDS.size();
+        final int numNewsNotifications = 1;
+        for (int i = 0; i < numNotifications; i++) {
+            NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, i, mUserId);
+            mService.addEnqueuedNotification(r);
+            Bundle signals = new Bundle();
+            final int adjustmentType = i + 1;
+            signals.putInt(Adjustment.KEY_TYPE, adjustmentType);
+            Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
+                    "", r.getUser().getIdentifier());
+            mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+            waitForIdle();
+            r.applyAdjustments();
+            r.setBundleType(adjustmentType);
+            // Check that the NotificationRecord channel is updated
+            assertThat(r.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+            assertThat(r.getBundleType()).isEqualTo(adjustmentType);
+        }
+
+        // Disable TYPE_NEWS bundle
+        mBinderService.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, false);
+        waitForIdle();
+
+        //Check that all notifications were unbundled
+        for (NotificationRecord record : mService.mEnqueuedNotifications) {
+            // Check that the original channel was restored
+            // for notifications classified as TYPE_NEWS
+            if (record.getBundleType() == TYPE_NEWS) {
+                assertThat(record.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+                verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(record), anyBoolean());
+            }
+        }
+
+        // Re-enable bundles for package
+        Mockito.reset(mRankingHandler);
+        Mockito.reset(mGroupHelper);
+        mBinderService.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true);
+        waitForIdle();
+
+        // Actually apply the adjustments
+        doAnswer(invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).applyAdjustments();
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).calculateImportance();
+            return null;
+        }).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
+        mService.handleRankingSort();
+
+        // Check that the bundle channel was restored
+        verify(mRankingHandler, times(numNewsNotifications)).requestSort();
+        for (NotificationRecord record : mService.mEnqueuedNotifications) {
+            record.applyAdjustments();
+            assertThat(record.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+        }
+    }
+
+    @Test
+    @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+            FLAG_NOTIFICATION_FORCE_GROUPING,
+            FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+    public void testDisallowTypeAdj_unclassifiesAllEnqueuedNotifications() throws Exception {
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+        when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
+
+        // Enqueue some notifications and classify in different bundles
+        final int numNotifications = NotificationChannel.SYSTEM_RESERVED_IDS.size();
+        for (int i = 0; i < numNotifications; i++) {
+            NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, i, mUserId);
+            mService.addEnqueuedNotification(r);
+            Bundle signals = new Bundle();
+            final int adjustmentType = i + 1;
+            signals.putInt(Adjustment.KEY_TYPE, adjustmentType);
+            Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
+                    "", r.getUser().getIdentifier());
+            mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+            waitForIdle();
+            r.applyAdjustments();
+            r.setBundleType(adjustmentType);
+            // Check that the NotificationRecord channel is updated
+            assertThat(r.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+            assertThat(r.getBundleType()).isEqualTo(adjustmentType);
+        }
+
+        // Disable KEY_TYPE adjustment
+        mBinderService.disallowAssistantAdjustment(Adjustment.KEY_TYPE);
+        waitForIdle();
+
+        //Check that all notifications were unbundled
+        for (NotificationRecord record : mService.mEnqueuedNotifications) {
+            assertThat(record.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+            verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(record), anyBoolean());
+        }
+
+        // Re-enable bundles
+        Mockito.reset(mRankingHandler);
+        Mockito.reset(mGroupHelper);
+        mBinderService.allowAssistantAdjustment(Adjustment.KEY_TYPE);
+        waitForIdle();
+
+        // Actually apply the adjustments
+        doAnswer(invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).applyAdjustments();
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).calculateImportance();
+            return null;
+        }).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
+        mService.handleRankingSort();
+
+        // Check that the bundle channel was restored
+        verify(mRankingHandler, times(numNotifications)).requestSort();
+        for (NotificationRecord record : mService.mEnqueuedNotifications) {
+            record.applyAdjustments();
+            assertThat(record.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
+        }
+    }
+
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java
index 9fe0e49..09ebc23 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java
@@ -29,12 +29,19 @@
 import android.service.notification.Adjustment;
 import android.service.notification.StatusBarNotification;
 
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
 import com.android.server.UiServiceTestCase;
 
+import org.junit.Rule;
 import org.junit.Test;
 
 public class NotificationRecordExtractorDataTest extends UiServiceTestCase {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Test
     public void testHasDiffs_noDiffs() {
         NotificationRecord r = generateRecord();
@@ -57,7 +64,8 @@
                 r.getRankingScore(),
                 r.isConversation(),
                 r.getProposedImportance(),
-                r.hasSensitiveContent());
+                r.hasSensitiveContent(),
+                r.getSummarization());
 
         assertFalse(extractorData.hasDiffForRankingLocked(r, 1));
         assertFalse(extractorData.hasDiffForLoggingLocked(r, 1));
@@ -85,7 +93,8 @@
                 r.getRankingScore(),
                 r.isConversation(),
                 r.getProposedImportance(),
-                r.hasSensitiveContent());
+                r.hasSensitiveContent(),
+                r.getSummarization());
 
         Bundle signals = new Bundle();
         signals.putInt(Adjustment.KEY_IMPORTANCE_PROPOSAL, IMPORTANCE_HIGH);
@@ -119,7 +128,8 @@
                 r.getRankingScore(),
                 r.isConversation(),
                 r.getProposedImportance(),
-                r.hasSensitiveContent());
+                r.hasSensitiveContent(),
+                r.getSummarization());
 
         Bundle signals = new Bundle();
         signals.putString(Adjustment.KEY_GROUP_KEY, "ranker_group");
@@ -154,7 +164,8 @@
                 r.getRankingScore(),
                 r.isConversation(),
                 r.getProposedImportance(),
-                r.hasSensitiveContent());
+                r.hasSensitiveContent(),
+                r.getSummarization());
 
         Bundle signals = new Bundle();
         signals.putBoolean(Adjustment.KEY_SENSITIVE_CONTENT, true);
@@ -166,6 +177,42 @@
         assertTrue(extractorData.hasDiffForLoggingLocked(r, 1));
     }
 
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NM_SUMMARIZATION)
+    public void testHasDiffs_summarization() {
+        NotificationRecord r = generateRecord();
+
+        NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData(
+                1,
+                r.getPackageVisibilityOverride(),
+                r.canShowBadge(),
+                r.canBubble(),
+                r.getNotification().isBubbleNotification(),
+                r.getChannel(),
+                r.getGroupKey(),
+                r.getPeopleOverride(),
+                r.getSnoozeCriteria(),
+                r.getUserSentiment(),
+                r.getSuppressedVisualEffects(),
+                r.getSystemGeneratedSmartActions(),
+                r.getSmartReplies(),
+                r.getImportance(),
+                r.getRankingScore(),
+                r.isConversation(),
+                r.getProposedImportance(),
+                r.hasSensitiveContent(),
+                r.getSummarization());
+
+        Bundle signals = new Bundle();
+        signals.putString(Adjustment.KEY_SUMMARIZATION, "SUMMARIZED!");
+        Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0);
+        r.addAdjustment(adjustment);
+        r.applyAdjustments();
+
+        assertTrue(extractorData.hasDiffForRankingLocked(r, 1));
+        assertTrue(extractorData.hasDiffForLoggingLocked(r, 1));
+    }
+
     private NotificationRecord generateRecord() {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
         final Notification.Builder builder = new Notification.Builder(getContext())
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 704b580..832ca51 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -260,7 +260,7 @@
         return FlagsParameterization.allCombinationsOf(
                 android.app.Flags.FLAG_API_RICH_ONGOING,
                 FLAG_NOTIFICATION_CLASSIFICATION, FLAG_NOTIFICATION_CLASSIFICATION_UI,
-                FLAG_MODES_UI);
+                FLAG_MODES_UI, android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS);
     }
 
     public PreferencesHelperTest(FlagsParameterization flags) {
@@ -3381,13 +3381,12 @@
         // user 0 records remain
         for (int i = 0; i < user0Uids.length; i++) {
             assertEquals(1,
-                    mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true)
-                            .getList().size());
+                    mHelper.getRemovedPkgNotificationChannels(PKG_N_MR1, user0Uids[i]).size());
         }
         // user 1 records are gone
         for (int i = 0; i < user1Uids.length; i++) {
-            assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false, true)
-                    .getList().size());
+            assertEquals(0,
+                    mHelper.getRemovedPkgNotificationChannels(PKG_N_MR1, user1Uids[i]).size());
         }
     }
 
@@ -3402,8 +3401,7 @@
         assertTrue(mHelper.onPackagesChanged(true, USER_SYSTEM, new String[]{PKG_N_MR1},
                 new int[]{UID_N_MR1}));
 
-        assertEquals(0, mHelper.getNotificationChannels(
-                PKG_N_MR1, UID_N_MR1, true, true).getList().size());
+        assertEquals(0, mHelper.getRemovedPkgNotificationChannels(PKG_N_MR1, UID_N_MR1).size());
 
         // Not deleted
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false,
@@ -3472,7 +3470,7 @@
         assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
         assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
         assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
-        assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size());
+        assertEquals(0, mHelper.getRemovedPkgNotificationChannels(PKG_O, UID_O).size());
         assertEquals(0, mHelper.getNotificationChannelGroups(PKG_O, UID_O).size());
 
         NotificationChannel channel = getChannel();
@@ -6836,38 +6834,11 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
-    public void testGetNotificationChannels_createIfNeeded() {
-        // Test setup hasn't created any channels or read package preferences yet.
-        // If we ask for notification channels _without_ creating, we should get no result.
-        ParceledListSlice<NotificationChannel> channels = mHelper.getNotificationChannels(PKG_N_MR1,
-                UID_N_MR1, false, false, /* createPrefsIfNeeded= */ false);
-        assertThat(channels.getList().size()).isEqualTo(0);
-
-        // If we ask it to create package preferences, we expect the default channel to be created
-        // for N_MR1.
-        channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false,
-                false, /* createPrefsIfNeeded= */ true);
-        assertThat(channels.getList().size()).isEqualTo(1);
-        assertThat(channels.getList().getFirst().getId()).isEqualTo(
-                NotificationChannel.DEFAULT_CHANNEL_ID);
-    }
-
-    @Test
     @DisableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
     public void testGetNotificationChannels_neverCreatesWhenFlagOff() {
-        ParceledListSlice<NotificationChannel> channels;
-        try {
-            channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false,
-                    false, /* createPrefsIfNeeded= */ true);
-        } catch (Exception e) {
-            // Slog.wtf kicks in, presumably
-        } finally {
-            channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false,
-                    false, /* createPrefsIfNeeded= */ false);
-            assertThat(channels.getList().size()).isEqualTo(0);
-        }
-
+        ParceledListSlice<NotificationChannel> channels = mHelper.getNotificationChannels(PKG_N_MR1,
+                UID_N_MR1, false, false);
+        assertThat(channels.getList().size()).isEqualTo(0);
     }
 
     // Test version of PreferencesHelper whose only functional difference is that it does not
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 82d87d4..ad900fe 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -48,13 +48,13 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
+import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.permission.PermissionManager;
 import android.telecom.TelecomManager;
-import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -152,7 +152,7 @@
 
         try {
             mService.init(mService.new WorkerHandler(mTestableLooper.getLooper()),
-                    mock(RankingHandler.class),
+                    mock(RankingHandler.class), new Handler(mTestableLooper.getLooper()),
                     mock(IPackageManager.class), mock(PackageManager.class),
                     mock(LightsManager.class),
                     mock(NotificationListeners.class), mock(NotificationAssistants.class),
diff --git a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
index 038e135..036e03c 100644
--- a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
@@ -23,6 +23,7 @@
 import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
 
 import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.Presubmit;
 import android.view.ViewConfiguration;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -38,6 +39,7 @@
  * Build/Install/Run:
  *  atest WmTests:CombinationKeyTests
  */
+@Presubmit
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER_MULTI_KEY_GESTURES)
diff --git a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
index ca3787e..bccdd67 100644
--- a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.platform.test.annotations.Presubmit;
 import android.view.KeyEvent;
 
 import org.junit.Before;
@@ -29,6 +30,7 @@
  *
  * <p>Build/Install/Run: atest WmTests:DeferredKeyActionExecutorTests
  */
+@Presubmit
 public final class DeferredKeyActionExecutorTests {
 
     private DeferredKeyActionExecutor mKeyActionExecutor;
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
index 8b5f68a..a912c17 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
@@ -29,6 +29,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
 import android.view.KeyEvent;
 
 import androidx.test.filters.SmallTest;
@@ -45,7 +46,7 @@
  * Build/Install/Run:
  *  atest KeyCombinationManagerTests
  */
-
+@Presubmit
 @SmallTest
 public class KeyCombinationManagerTests {
     private KeyCombinationManager mKeyCombinationManager;
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index 85ef466..c6b431c 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -176,10 +176,10 @@
                         KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
                         KeyEvent.KEYCODE_NOTIFICATION,
                         0},
-                {"Meta + Ctrl + S -> Take Screenshot",
-                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_S},
+                {"Meta + S -> Take Screenshot",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_S},
                         KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, KeyEvent.KEYCODE_S,
-                        META_ON | CTRL_ON},
+                        META_ON},
                 {"Meta + / -> Open Shortcut Helper", new int[]{META_KEY, KeyEvent.KEYCODE_SLASH},
                         KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
                         KeyEvent.KEYCODE_SLASH, META_ON},
@@ -544,17 +544,6 @@
     }
 
     @Test
-    public void testKeyGestureSplitscreenFocus() {
-        Assert.assertTrue(sendKeyGestureEventComplete(
-                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT));
-        mPhoneWindowManager.assertSetSplitscreenFocus(true);
-
-        Assert.assertTrue(sendKeyGestureEventComplete(
-                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT));
-        mPhoneWindowManager.assertSetSplitscreenFocus(false);
-    }
-
-    @Test
     public void testKeyGestureShortcutHelper() {
         Assert.assertTrue(sendKeyGestureEventComplete(
                 KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER));
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index 82a5add..d961a6a 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -42,6 +42,7 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
@@ -64,7 +65,7 @@
  * Build/Install/Run:
  *  atest ModifierShortcutManagerTests
  */
-
+@Presubmit
 @SmallTest
 @EnableFlags(com.android.hardware.input.Flags.FLAG_MODIFIER_SHORTCUT_MANAGER_REFACTOR)
 public class ModifierShortcutManagerTests {
@@ -127,7 +128,7 @@
         // Total valid shortcuts.
         KeyboardShortcutGroup group =
                 mModifierShortcutManager.getApplicationLaunchKeyboardShortcuts(-1);
-        assertEquals(13, group.getItems().size());
+        assertEquals(11, group.getItems().size());
 
         // Total valid shift shortcuts.
         assertEquals(3, group.getItems().stream()
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index af3dc1d..c73ce23 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -48,6 +48,7 @@
 import android.content.Context;
 import android.hardware.input.InputManager;
 import android.os.PowerManager;
+import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.filters.SmallTest;
@@ -72,6 +73,7 @@
  * Build/Install/Run:
  * atest WmTests:PhoneWindowManagerTests
  */
+@Presubmit
 @SmallTest
 public class PhoneWindowManagerTests {
 
diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
index 33ccec3..53e82ba 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
@@ -28,6 +28,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 import android.view.Display;
 
@@ -40,6 +41,7 @@
  * Build/Install/Run:
  *  atest WmTests:PowerKeyGestureTests
  */
+@Presubmit
 public class PowerKeyGestureTests extends ShortcutKeyTestBase {
     @Before
     public void setUp() {
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 08eb145..6c65942 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -39,6 +39,7 @@
 import android.os.SystemClock;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.KeyEvent;
 
@@ -57,6 +58,7 @@
  * Build/Install/Run:
  *  atest WmTests:SingleKeyGestureTests
  */
+@Presubmit
 public class SingleKeyGestureTests {
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index 3ea3235..833dd5d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -35,6 +35,7 @@
 import android.content.ComponentName;
 import android.hardware.input.KeyGestureEvent;
 import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 import android.view.Display;
 
@@ -47,6 +48,7 @@
  * Build/Install/Run:
  * atest WmTests:StemKeyGestureTests
  */
+@Presubmit
 public class StemKeyGestureTests extends ShortcutKeyTestBase {
 
     private static final String TEST_TARGET_ACTIVITY = "com.android.server.policy/.TestActivity";
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 4ff3d43..f884924 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -925,11 +925,6 @@
         verify(mStatusBarManagerInternal).moveFocusedTaskToStageSplit(anyInt(), eq(leftOrTop));
     }
 
-    void assertSetSplitscreenFocus(boolean leftOrTop) {
-        mTestLooper.dispatchAll();
-        verify(mStatusBarManagerInternal).setSplitscreenFocus(eq(leftOrTop));
-    }
-
     void assertStatusBarStartAssist() {
         mTestLooper.dispatchAll();
         verify(mStatusBarManagerInternal).startAssist(any());
diff --git a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
index 3ca352c..9e59bce 100644
--- a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
@@ -57,6 +57,7 @@
 import android.os.PowerManager;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.view.Display;
@@ -83,6 +84,7 @@
  *
  * <p>Build/Install/Run: atest WmTests:WindowWakeUpPolicyTests
  */
+@Presubmit
 public final class WindowWakeUpPolicyTests {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java
new file mode 100644
index 0000000..e0b700a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.window.DesktopModeFlags;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.R;
+import com.android.window.flags.Flags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+
+/**
+ * Test class for {@link DesktopModeHelper}.
+ */
+@SmallTest
+@Presubmit
+@EnableFlags(Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+public class DesktopModeHelperTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private Context mContext;
+    private Context mMockContext;
+    private Resources mMockResources;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        mMockContext = mock(Context.class);
+        mMockResources = mock(Resources.class);
+
+        doReturn(mMockResources).when(mMockContext).getResources();
+        doReturn(false).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+        doReturn(false).when(mMockResources).getBoolean(
+                eq(R.bool.config_isDesktopModeDevOptionSupported));
+        doReturn(mContext.getContentResolver()).when(mMockContext).getContentResolver();
+        resetDesktopModeFlagsCache();
+        resetEnforceDeviceRestriction();
+        resetFlagOverride();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        resetDesktopModeFlagsCache();
+        resetEnforceDeviceRestriction();
+        resetFlagOverride();
+    }
+
+    @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION})
+    @Test
+    public void canEnterDesktopMode_DWFlagDisabled_configsOff_returnsFalse() {
+        assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse();
+    }
+
+    @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION})
+    @Test
+    public void canEnterDesktopMode_DWFlagDisabled_configsOn_disableDeviceCheck_returnsFalse()
+            throws Exception {
+        doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+        doReturn(true).when(mMockResources).getBoolean(
+                eq(R.bool.config_isDesktopModeDevOptionSupported));
+        disableEnforceDeviceRestriction();
+
+        assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse();
+    }
+
+    @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION})
+    @Test
+    public void canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_returnsFalse() {
+        doReturn(true).when(mMockResources).getBoolean(
+                eq(R.bool.config_isDesktopModeDevOptionSupported));
+
+        assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse();
+    }
+
+    @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION})
+    @Test
+    public void canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_flagOverrideOn_returnsTrue()
+            throws Exception {
+        doReturn(true).when(mMockResources).getBoolean(
+                eq(R.bool.config_isDesktopModeDevOptionSupported));
+        setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON);
+
+        assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue();
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    public void canEnterDesktopMode_DWFlagEnabled_configsOff_returnsFalse() {
+        assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse();
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    public void canEnterDesktopMode_DWFlagEnabled_configDesktopModeOff_returnsFalse() {
+        doReturn(true).when(mMockResources).getBoolean(
+                eq(R.bool.config_isDesktopModeDevOptionSupported));
+
+        assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse();
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    public void canEnterDesktopMode_DWFlagEnabled_configDesktopModeOn_returnsTrue() {
+        doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+
+        assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue();
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    public void canEnterDesktopMode_DWFlagEnabled_configsOff_disableDeviceRestrictions_returnsTrue()
+            throws Exception {
+        disableEnforceDeviceRestriction();
+
+        assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue();
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    public void canEnterDesktopMode_DWFlagEnabled_configDevOptionOn_flagOverrideOn_returnsTrue() {
+        doReturn(true).when(mMockResources).getBoolean(
+                eq(R.bool.config_isDesktopModeDevOptionSupported)
+        );
+        setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON);
+
+        assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue();
+    }
+
+    @Test
+    public void isDeviceEligibleForDesktopMode_configDEModeOn_returnsTrue() {
+        doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+
+        assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue();
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @Test
+    public void isDeviceEligibleForDesktopMode_supportFlagOff_returnsFalse() {
+        assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isFalse();
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @Test
+    public void isDeviceEligibleForDesktopMode_supportFlagOn_returnsFalse() {
+        assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isFalse();
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+    @Test
+    public void isDeviceEligibleForDesktopMode_supportFlagOn_configDevOptModeOn_returnsTrue() {
+        doReturn(true).when(mMockResources).getBoolean(
+                eq(R.bool.config_isDesktopModeDevOptionSupported)
+        );
+
+        assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue();
+    }
+
+    private void resetEnforceDeviceRestriction() throws Exception {
+        setEnforceDeviceRestriction(true);
+    }
+
+    private void disableEnforceDeviceRestriction() throws Exception {
+        setEnforceDeviceRestriction(false);
+    }
+
+    private void setEnforceDeviceRestriction(boolean value) throws Exception {
+        Field deviceRestriction = DesktopModeHelper.class.getDeclaredField(
+                "ENFORCE_DEVICE_RESTRICTIONS");
+        deviceRestriction.setAccessible(true);
+        deviceRestriction.setBoolean(/* obj= */ null, /* z= */ value);
+    }
+
+    private void resetDesktopModeFlagsCache() throws Exception {
+        Field cachedToggleOverride = DesktopModeFlags.class.getDeclaredField(
+                "sCachedToggleOverride");
+        cachedToggleOverride.setAccessible(true);
+        cachedToggleOverride.set(/* obj= */ null, /* value= */ null);
+    }
+
+    private void resetFlagOverride() {
+        Settings.Global.putString(mContext.getContentResolver(),
+                DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, null);
+    }
+
+    private void setFlagOverride(DesktopModeFlags.ToggleOverride override) {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, override.getSetting());
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 5ac3e48..c3aa289 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4461,7 +4461,46 @@
         // are aligned to the top of the parentAppBounds
         assertEquals(new Rect(0, notchHeight, 1000, 1200), appBounds);
         assertEquals(new Rect(0, 0, 1000, 1200), bounds);
+    }
 
+    @Test
+    @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
+    public void testInFreeform_boundsSandboxedToAppBounds() {
+        final int dw = 2800;
+        final int dh = 1400;
+        final int notchHeight = 100;
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm, dw, dh)
+                .setNotch(notchHeight)
+                .build();
+        setUpApp(display);
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        mTask.mDisplayContent.getDefaultTaskDisplayArea()
+                .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+        mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        Rect appBounds = new Rect(0, 0, 1000, 500);
+        Rect bounds = new Rect(0, 0, 1000, 600);
+        mTask.getWindowConfiguration().setAppBounds(appBounds);
+        mTask.getWindowConfiguration().setBounds(bounds);
+        mActivity.onConfigurationChanged(mTask.getConfiguration());
+
+        // Bounds are sandboxed to appBounds in freeform.
+        assertDownScaled();
+        assertEquals(mActivity.getWindowConfiguration().getAppBounds(),
+                mActivity.getWindowConfiguration().getBounds());
+
+        // Exit freeform.
+        mTask.mDisplayContent.getDefaultTaskDisplayArea()
+                .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+        mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        mTask.getWindowConfiguration().setBounds(new Rect(0, 0, dw, dh));
+        mActivity.onConfigurationChanged(mTask.getConfiguration());
+        assertFitted();
+        appBounds = mActivity.getWindowConfiguration().getAppBounds();
+        bounds = mActivity.getWindowConfiguration().getBounds();
+        // Bounds are not sandboxed to appBounds.
+        assertNotEquals(appBounds, bounds);
+        assertEquals(notchHeight, appBounds.top - bounds.top);
     }
 
     @Test
@@ -4914,7 +4953,8 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+            Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
     public void testCameraCompatAspectRatioAppliedForFixedOrientationCameraActivities() {
         // Needed to create camera compat policy in DisplayContent.
         allowDesktopMode();
@@ -4926,7 +4966,8 @@
         setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
 
         // Create task on test display.
-        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
 
         // Create fixed portrait activity.
         final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
@@ -4939,7 +4980,8 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+            Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
     public void testCameraCompatAspectRatioForFixedOrientationCameraActivitiesPortraitWindow() {
         // Needed to create camera compat policy in DisplayContent.
         allowDesktopMode();
@@ -4951,7 +4993,8 @@
         setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
 
         // Create task on test display.
-        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
 
         // Create fixed portrait activity.
         final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
@@ -4964,7 +5007,8 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+            Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
     public void testCameraCompatAspectRatioAppliedInsteadOfDefaultAspectRatio() {
         // Needed to create camera compat policy in DisplayContent.
         allowDesktopMode();
@@ -4976,7 +5020,8 @@
         setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
 
         // Create task on test display.
-        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
 
         // App's target min aspect ratio - this should not be used, as camera controls aspect ratio.
         final float targetMinAspectRatio = 4.0f;
@@ -4993,7 +5038,8 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+            Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
     public void testCameraCompatAspectRatio_defaultAspectRatioAppliedWhenGreater() {
         // Needed to create camera compat policy in DisplayContent.
         allowDesktopMode();
@@ -5005,7 +5051,8 @@
         setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
 
         // Create task on test display.
-        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
 
         // App's target min aspect ratio bigger than camera compat aspect ratio - use that instead.
         final float targetMinAspectRatio = 6.0f;
diff --git a/services/usb/OWNERS b/services/usb/OWNERS
index 2dff392..2592612 100644
--- a/services/usb/OWNERS
+++ b/services/usb/OWNERS
@@ -1,9 +1,9 @@
-anothermark@google.com
+vmartensson@google.com
+nkapron@google.com
 febinthattil@google.com
-aprasath@google.com
+shubhankarm@google.com
 badhri@google.com
 elaurent@google.com
 albertccwang@google.com
 jameswei@google.com
 howardyen@google.com
-kumarashishg@google.com
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
index a03d7e2..69dd556 100644
--- a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
+++ b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
@@ -112,7 +112,7 @@
         if (DBG) Log.d(LOG_TAG, "Trying to get current content resolver...");
 
         final int currentUser = ActivityManager.getCurrentUser();
-        final int myUser = UserManager.get(context).getProcessUserId();
+        final int myUser = UserHandle.myUserId();
 
         if (DBG) Log.d(LOG_TAG, "myUser=" + myUser + "currentUser=" + currentUser);
 
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index b32379a..4d9df46 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -415,4 +415,5 @@
      */
     boolean hasForegroundServiceDelegation(in PhoneAccountHandle phoneAccountHandle,
                                                        String callingPackage);
+    void setMetricsTestMode(boolean enabled);
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 6d32303..73ea68b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -19380,7 +19380,6 @@
      *
      * @hide
      */
-    @FlaggedApi(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY)
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi
     public void setEnableCellularIdentifierDisclosureNotifications(boolean enable) {
@@ -19406,7 +19405,6 @@
      *
      * @hide
      */
-    @FlaggedApi(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY)
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @SystemApi
     public boolean isCellularIdentifierDisclosureNotificationsEnabled() {
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
index 9d9cac9..d62fd63 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
@@ -22,8 +22,10 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Rlog;
 
 import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -220,7 +222,7 @@
         StringBuilder sb = new StringBuilder();
 
         sb.append("SubscriberId:");
-        sb.append(mSubscriberId);
+        sb.append(Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mSubscriberId));
         sb.append(",");
 
         sb.append("CarrierId:");
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
index fb4f89d..75d3ec6 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
@@ -21,8 +21,10 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Rlog;
 
 import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.util.Objects;
 
@@ -132,7 +134,7 @@
         StringBuilder sb = new StringBuilder();
 
         sb.append("SatelliteSubscriberInfo:");
-        sb.append(mSubscriberInfo);
+        sb.append(Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mSubscriberInfo));
         sb.append(",");
 
         sb.append("ProvisionStatus:");
diff --git a/telephony/java/com/android/internal/telephony/util/WorkerThread.java b/telephony/java/com/android/internal/telephony/util/WorkerThread.java
new file mode 100644
index 0000000..f5b6536
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/util/WorkerThread.java
@@ -0,0 +1,130 @@
+/*
+ * 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.telephony.util;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+/**
+ * Shared singleton worker thread for each process.
+ *
+ * This thread should be used for work that needs to be executed at standard priority
+ * but not on the main thread. This is suitable for handling asynchronous tasks that
+ * are ephemeral or require enough work that they shouldn't block the main thread, but
+ * should not block each other for more than around 100ms.
+ */
+public final class WorkerThread extends HandlerThread {
+    private static volatile WorkerThread sInstance;
+    private static volatile Handler sHandler;
+    private static volatile HandlerExecutor sHandlerExecutor;
+    private static final Object sLock = new Object();
+
+    private CountDownLatch mInitLock = new CountDownLatch(1);
+
+
+    private WorkerThread() {
+        super("android.telephony.worker");
+    }
+
+    private static void ensureThread() {
+        if (sInstance != null) return;
+        synchronized (sLock) {
+            if (sInstance != null) return;
+
+            final WorkerThread tmpThread = new WorkerThread();
+            tmpThread.start();
+
+            try {
+                tmpThread.mInitLock.await();
+            } catch (InterruptedException ignored) {
+            }
+
+
+            sHandler = new Handler(
+                    tmpThread.getLooper(),
+                    /* callback= */ null,
+                    /* async= */ false,
+                    /* shared= */ true);
+            sHandlerExecutor = new HandlerExecutor(sHandler);
+            sInstance = tmpThread; // Note: order matters here. sInstance must be assigned last.
+
+        }
+    }
+
+    @Override
+    protected void onLooperPrepared() {
+        mInitLock.countDown();
+    }
+
+    /**
+     * Get the worker thread directly.
+     *
+     * Users of this thread should take care not to block it for extended periods of
+     * time.
+     *
+     * @return a HandlerThread, never null
+     */
+    @NonNull public static HandlerThread get() {
+        ensureThread();
+        return sInstance;
+    }
+
+    /**
+     * Get a Handler that can process Runnables.
+     *
+     * @return a Handler, never null
+     */
+    @NonNull public static Handler getHandler() {
+        ensureThread();
+        return sHandler;
+    }
+
+    /**
+     * Get an Executor that can process Runnables
+     *
+     * @return an Executor, never null
+     */
+    @NonNull public static Executor getExecutor() {
+        ensureThread();
+        return sHandlerExecutor;
+    }
+
+    /**
+     * A method to reset the WorkerThread from scratch.
+     *
+     * This method should only be used for unit testing. In production it would have
+     * catastrophic consequences. Do not ever use this outside of tests.
+     */
+    @VisibleForTesting
+    public static void reset() {
+        synchronized (sLock) {
+            if (sInstance == null) return;
+            sInstance.quitSafely();
+            sInstance = null;
+            sHandler = null;
+            sHandlerExecutor = null;
+            ensureThread();
+        }
+    }
+}
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
index a0e0477..1fb18a6 100644
--- a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
+++ b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
@@ -39,13 +39,4 @@
     device_common_data: [
         ":cdm_snippet_legacy",
     ],
-    version: {
-        py2: {
-            enabled: false,
-        },
-        py3: {
-            enabled: true,
-            embedded_launcher: true,
-        },
-    },
 }
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..def254a 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
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.notification
 
-import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.Presubmit
 import android.platform.test.rule.SettingOverrideRule
 import android.provider.Settings
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
@@ -26,6 +24,7 @@
 import android.tools.flicker.legacy.LegacyFlickerTestFactory
 import android.tools.helpers.wakeUpAndGoToHomeScreen
 import android.tools.traces.component.ComponentNameMatcher
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import org.junit.ClassRule
 import org.junit.FixMethodOrder
@@ -46,7 +45,7 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Postsubmit
+@FlakyTest(bugId = 384046002)
 open class OpenAppFromLockscreenNotificationColdTest(flicker: LegacyFlickerTest) :
     OpenAppFromNotificationColdTest(flicker) {
 
@@ -107,12 +106,10 @@
     override fun navBarWindowIsAlwaysVisible() {}
 
     /** {@inheritDoc} */
-    @Postsubmit
     @Test
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
         super.visibleLayersShownMoreThanOneConsecutiveEntry()
 
-    @Presubmit
     @Test
     override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         flicker.assertWm {
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..7da529d 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
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.notification
 
-import android.platform.test.annotations.Presubmit
 import android.platform.test.rule.SettingOverrideRule
 import android.provider.Settings
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
@@ -46,6 +45,7 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 384046002)
 class OpenAppFromLockscreenNotificationWarmTest(flicker: LegacyFlickerTest) :
     OpenAppFromNotificationWarmTest(flicker) {
 
@@ -72,7 +72,6 @@
      * window of the transition, with snapshot or splash screen windows optionally showing first.
      */
     @Test
-    @Presubmit
     fun appWindowBecomesFirstAndOnlyTopWindow() {
         flicker.assertWm {
             this.hasNoVisibleAppWindow()
@@ -87,7 +86,6 @@
 
     /** Checks that the screen is locked at the start of the transition */
     @Test
-    @Presubmit
     fun screenLockedStart() {
         flicker.assertWmStart { isKeyguardShowing() }
     }
@@ -117,7 +115,7 @@
      * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
      * transition
      */
-    @Presubmit @Test fun statusBarLayerPositionAtEnd() = flicker.statusBarLayerPositionAtEnd()
+    @Test fun statusBarLayerPositionAtEnd() = flicker.statusBarLayerPositionAtEnd()
 
     /** {@inheritDoc} */
     @Test
@@ -140,7 +138,6 @@
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
         super.visibleLayersShownMoreThanOneConsecutiveEntry()
 
-    @Presubmit
     @Test
     override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         flicker.assertWm {
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..76b43b2 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
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.notification
 
-import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.Presubmit
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
 import android.tools.flicker.legacy.LegacyFlickerTest
@@ -47,7 +45,7 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Postsubmit
+@FlakyTest(bugId = 384046002)
 class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: LegacyFlickerTest) :
     OpenAppFromLockscreenNotificationColdTest(flicker) {
     private val showWhenLockedApp = ShowWhenLockedAppHelper(instrumentation)
@@ -106,7 +104,7 @@
     }
 
     /** {@inheritDoc} */
-    @Presubmit @Test override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
+    @Test override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
 
     /** {@inheritDoc} */
     @FlakyTest(bugId = 227143265)
@@ -120,7 +118,6 @@
     override fun navBarLayerIsVisibleAtStartAndEnd() {}
 
     /** {@inheritDoc} */
-    @Presubmit
     @Test
     override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
 
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..39302d8 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
@@ -16,14 +16,13 @@
 
 package com.android.server.wm.flicker.notification
 
-import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.Presubmit
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
 import android.tools.flicker.legacy.LegacyFlickerTest
 import android.tools.flicker.legacy.LegacyFlickerTestFactory
 import android.tools.helpers.wakeUpAndGoToHomeScreen
 import android.tools.traces.component.ComponentNameMatcher
+import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
 import org.junit.FixMethodOrder
@@ -41,7 +40,7 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Postsubmit
+@FlakyTest(bugId = 384046002)
 open class OpenAppFromNotificationColdTest(flicker: LegacyFlickerTest) :
     OpenAppFromNotificationWarmTest(flicker) {
     /** {@inheritDoc} */
@@ -59,9 +58,9 @@
             teardown { testApp.exit(wmHelper) }
         }
 
-    @Presubmit @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
+    @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
 
-    @Presubmit @Test override fun appLayerBecomesVisible() = appLayerBecomesVisible_coldStart()
+    @Test override fun appLayerBecomesVisible() = appLayerBecomesVisible_coldStart()
 
     /** {@inheritDoc} */
     @Test
@@ -83,7 +82,7 @@
      * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
      * transition
      */
-    @Presubmit @Test open fun statusBarLayerPositionAtEnd() = flicker.statusBarLayerPositionAtEnd()
+    @Test open fun statusBarLayerPositionAtEnd() = flicker.statusBarLayerPositionAtEnd()
 
     /** {@inheritDoc} */
     @Test
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 ad70757..f1e1b6f 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
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.notification
 
-import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.Presubmit
 import android.platform.test.rule.DisableNotificationCooldownSettingRule
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
@@ -28,6 +26,7 @@
 import android.tools.traces.component.ComponentNameMatcher
 import android.view.WindowInsets
 import android.view.WindowManager
+import androidx.test.filters.FlakyTest
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.Until
 import com.android.server.wm.flicker.helpers.NotificationAppHelper
@@ -54,6 +53,7 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 384046002)
 open class OpenAppFromNotificationWarmTest(flicker: LegacyFlickerTest) :
     OpenAppTransition(flicker) {
     override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation)
@@ -120,23 +120,20 @@
         // Wait for the app to launch
         wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
     }
-    @Presubmit @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_warmStart()
+    @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_warmStart()
 
-    @Presubmit @Test override fun appLayerBecomesVisible() = appLayerBecomesVisible_warmStart()
+    @Test override fun appLayerBecomesVisible() = appLayerBecomesVisible_warmStart()
 
-    @Presubmit
     @Test
     open fun notificationAppWindowVisibleAtEnd() {
         flicker.assertWmEnd { this.isAppWindowVisible(testApp) }
     }
 
-    @Presubmit
     @Test
     open fun notificationAppWindowOnTopAtEnd() {
         flicker.assertWmEnd { this.isAppWindowOnTop(testApp) }
     }
 
-    @Presubmit
     @Test
     open fun notificationAppLayerVisibleAtEnd() {
         flicker.assertLayersEnd { this.isVisible(testApp) }
@@ -148,7 +145,6 @@
      *
      * Note: Large screen only
      */
-    @Presubmit
     @Test
     open fun taskBarWindowIsVisibleAtEnd() {
         Assume.assumeTrue(usesTaskbar)
@@ -160,7 +156,6 @@
      *
      * Note: Large screen only
      */
-    @Presubmit
     @Test
     open fun taskBarLayerIsVisibleAtEnd() {
         Assume.assumeTrue(usesTaskbar)
@@ -168,7 +163,6 @@
     }
 
     /** Checks the position of the [ComponentNameMatcher.NAV_BAR] at the end of the transition */
-    @Presubmit
     @Test
     open fun navBarLayerPositionAtEnd() {
         Assume.assumeFalse(usesTaskbar)
@@ -176,14 +170,12 @@
     }
 
     /** {@inheritDoc} */
-    @Presubmit
     @Test
     open fun navBarLayerIsVisibleAtEnd() {
         Assume.assumeFalse(usesTaskbar)
         flicker.navBarLayerIsVisibleAtEnd()
     }
 
-    @Presubmit
     @Test
     open fun navBarWindowIsVisibleAtEnd() {
         Assume.assumeFalse(usesTaskbar)
@@ -197,7 +189,6 @@
 
     /** {@inheritDoc} */
     @Test
-    @Postsubmit
     override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
 
     companion object {
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
index 4ba444b..e825af9 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
@@ -16,10 +16,10 @@
 
 package com.android.server.wm.flicker.notification
 
-import android.platform.test.annotations.Presubmit
 import android.tools.device.apphelpers.StandardAppHelper
 import android.tools.flicker.legacy.LegacyFlickerTest
 import android.tools.traces.component.ComponentNameMatcher
+import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.BaseTest
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import org.junit.Test
@@ -41,7 +41,7 @@
      * Checks that the [testApp] layer doesn't exist or is invisible at the start of the transition,
      * but is created and/or becomes visible during the transition.
      */
-    @Presubmit
+    @FlakyTest(bugId = 384046002)
     @Test
     open fun appLayerBecomesVisible() {
         appLayerBecomesVisible_coldStart()
@@ -80,7 +80,7 @@
      * The `isAppWindowInvisible` step is optional because we log once per frame, upon logging, the
      * window may be visible or not depending on what was processed until that moment.
      */
-    @Presubmit @Test open fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
+    @FlakyTest(bugId = 384046002) @Test open fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
 
     protected fun appWindowBecomesVisible_coldStart() {
         flicker.assertWm {
@@ -108,7 +108,7 @@
      * Checks that [testApp] window is not on top at the start of the transition, and then becomes
      * the top visible window until the end of the transition.
      */
-    @Presubmit
+    @FlakyTest(bugId = 384046002)
     @Test
     open fun appWindowBecomesTopWindow() {
         flicker.assertWm {
@@ -124,7 +124,7 @@
      * Checks that [testApp] window is not on top at the start of the transition, and then becomes
      * the top visible window until the end of the transition.
      */
-    @Presubmit
+    @FlakyTest(bugId = 384046002)
     @Test
     open fun appWindowIsTopWindowAtEnd() {
         flicker.assertWmEnd { this.isAppWindowOnTop(testApp) }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 851ce02..18f44dd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -18,7 +18,9 @@
 
 import android.app.Instrumentation
 import android.content.Intent
+import android.os.UserHandle
 import android.platform.test.annotations.Presubmit
+import android.provider.Settings
 import android.tools.flicker.junit.FlickerBuilderProvider
 import android.tools.flicker.legacy.FlickerBuilder
 import android.tools.flicker.legacy.LegacyFlickerTest
@@ -45,6 +47,12 @@
 ) {
     init {
         tapl.setExpectedRotationCheckEnabled(true)
+        Settings.System.putIntForUser(
+            instrumentation.targetContext.contentResolver,
+            Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
+            0,
+            UserHandle.USER_CURRENT_OR_SELF
+        )
     }
 
     private val logTag = this::class.java.simpleName
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/RecentTasksUtils.kt
similarity index 94%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/RecentTasksUtils.kt
index aa262f9..1a5fda7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/RecentTasksUtils.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.utils
+package com.android.server.wm.flicker.helpers
 
 import android.app.Instrumentation
 
@@ -24,4 +24,4 @@
             "dumpsys activity service SystemUIService WMShell recents clearAll"
         )
     }
-}
\ No newline at end of file
+}
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
index 1c2a053..c2f9adf 100644
--- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -50,10 +50,7 @@
  */
 @Presubmit
 @RunWith(MockitoJUnitRunner::class)
-@EnableFlags(
-    com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
-    com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL,
-)
+@EnableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG)
 class StickyModifierStateListenerTest {
 
     @get:Rule
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index e053263..36db955 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -383,15 +383,14 @@
                 intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
             ),
             TestData(
-                "META + CTRL + S -> Take Screenshot",
+                "META + S -> Take Screenshot",
                 intArrayOf(
                     KeyEvent.KEYCODE_META_LEFT,
-                    KeyEvent.KEYCODE_CTRL_LEFT,
                     KeyEvent.KEYCODE_S
                 ),
                 KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
                 intArrayOf(KeyEvent.KEYCODE_S),
-                KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+                KeyEvent.META_META_ON,
                 intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
             ),
             TestData(
@@ -467,30 +466,6 @@
                 intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
             ),
             TestData(
-                "CTRL + ALT + DPAD_LEFT -> Change Splitscreen Focus Left",
-                intArrayOf(
-                    KeyEvent.KEYCODE_CTRL_LEFT,
-                    KeyEvent.KEYCODE_ALT_LEFT,
-                    KeyEvent.KEYCODE_DPAD_LEFT
-                ),
-                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
-                intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
-                KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
-                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
-            ),
-            TestData(
-                "CTRL + ALT + DPAD_RIGHT -> Change Splitscreen Focus Right",
-                intArrayOf(
-                    KeyEvent.KEYCODE_CTRL_LEFT,
-                    KeyEvent.KEYCODE_ALT_LEFT,
-                    KeyEvent.KEYCODE_DPAD_RIGHT
-                ),
-                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
-                intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
-                KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
-                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
-            ),
-            TestData(
                 "META + / -> Open Shortcut Helper",
                 intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_SLASH),
                 KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index d6654cc..4440a83 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -28,6 +28,8 @@
 import android.hardware.input.InputManagerGlobal
 import android.hardware.input.KeyboardLayout
 import android.hardware.input.KeyboardLayoutSelectionResult
+import android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE
+import android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER
 import android.icu.util.ULocale
 import android.os.Bundle
 import android.os.test.TestLooper
@@ -281,6 +283,41 @@
     }
 
     @Test
+    fun testGetSetKeyboardLayoutOverrideForInputDevice() {
+        val imeSubtype = createImeSubtype()
+
+        keyboardLayoutManager.setKeyboardLayoutOverrideForInputDevice(
+            keyboardDevice.identifier,
+            ENGLISH_UK_LAYOUT_DESCRIPTOR
+        )
+        var result =
+            keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+                keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
+            )
+        assertEquals(LAYOUT_SELECTION_CRITERIA_DEVICE, result.selectionCriteria)
+        assertEquals(
+            "getKeyboardLayoutForInputDevice API should return the set layout",
+            ENGLISH_UK_LAYOUT_DESCRIPTOR,
+            result.layoutDescriptor
+        )
+
+        // This should replace the overriding layout set above
+        keyboardLayoutManager.setKeyboardLayoutForInputDevice(
+            keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
+            ENGLISH_US_LAYOUT_DESCRIPTOR
+        )
+        result = keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+            keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
+        )
+        assertEquals(LAYOUT_SELECTION_CRITERIA_USER, result.selectionCriteria)
+        assertEquals(
+            "getKeyboardLayoutForInputDevice API should return the user set layout",
+            ENGLISH_US_LAYOUT_DESCRIPTOR,
+            result.layoutDescriptor
+        )
+    }
+
+    @Test
     fun testGetKeyboardLayoutListForInputDevice() {
         // Check Layouts for "hi-Latn". It should return all 'Latn' keyboard layouts
         var keyboardLayouts =
diff --git a/tests/testables/src/android/testing/OWNERS b/tests/testables/src/android/testing/OWNERS
new file mode 100644
index 0000000..f31666b
--- /dev/null
+++ b/tests/testables/src/android/testing/OWNERS
@@ -0,0 +1,2 @@
+# MessageQueue-related classes
+per-file TestableLooper.java = mfasheh@google.com, shayba@google.com
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index be5c84c..3ee6dc4 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -42,6 +42,12 @@
  * and provide an easy annotation for use with tests.
  *
  * @see TestableLooperTest TestableLooperTest for examples.
+ *
+ * @deprecated Use {@link android.os.TestLooperManager} or {@link
+ *     org.robolectric.shadows.ShadowLooper} instead.
+ *     This class is not actively maintained.
+ *     Both of the recommended alternatives allow fine control of execution.
+ *     The Robolectric class also allows advancing time.
  */
 public class TestableLooper {
 
diff --git a/tests/utils/testutils/java/android/os/test/OWNERS b/tests/utils/testutils/java/android/os/test/OWNERS
index 3a9129e..6448261 100644
--- a/tests/utils/testutils/java/android/os/test/OWNERS
+++ b/tests/utils/testutils/java/android/os/test/OWNERS
@@ -1 +1,4 @@
 per-file FakePermissionEnforcer.java = file:/tests/EnforcePermission/OWNERS
+
+# MessageQueue-related classes
+per-file TestLooper.java = mfasheh@google.com, shayba@google.com
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index 56b0a25..61fa7b5 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -18,27 +18,41 @@
 
 import static org.junit.Assert.assertTrue;
 
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
 import android.os.SystemClock;
+import android.os.TestLooperManager;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.ArrayDeque;
+import java.util.Queue;
 import java.util.concurrent.Executor;
 
 /**
- * Creates a looper whose message queue can be manipulated
- * This allows testing code that uses a looper to dispatch messages in a deterministic manner
- * Creating a TestLooper will also install it as the looper for the current thread
+ * Creates a looper whose message queue can be manipulated This allows testing code that uses a
+ * looper to dispatch messages in a deterministic manner Creating a TestLooper will also install it
+ * as the looper for the current thread
+ *
+ * @deprecated Use {@link android.os.TestLooperManager} or {@link
+ *     org.robolectric.shadows.ShadowLooper} instead.
+ *     This class is not actively maintained.
+ *     Both of the recommended alternatives allow fine control of execution.
+ *     The Robolectric class also allows advancing time.
  */
 public class TestLooper {
-    protected final Looper mLooper;
+    private final Looper mLooper;
+    private final TestLooperManager mTestLooperManager;
+    private final Clock mClock;
 
     private static final Constructor<Looper> LOOPER_CONSTRUCTOR;
     private static final Field THREAD_LOCAL_LOOPER_FIELD;
@@ -48,24 +62,37 @@
     private static final Method MESSAGE_MARK_IN_USE_METHOD;
     private static final String TAG = "TestLooper";
 
-    private final Clock mClock;
-
     private AutoDispatchThread mAutoDispatchThread;
 
+    /**
+     * Baklava introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
+     */
+    private static boolean isAtLeastBaklava() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
+    }
+
     static {
         try {
             LOOPER_CONSTRUCTOR = Looper.class.getDeclaredConstructor(Boolean.TYPE);
             LOOPER_CONSTRUCTOR.setAccessible(true);
             THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
             THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
-            MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
-            MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
-            MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
-            MESSAGE_NEXT_FIELD.setAccessible(true);
-            MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
-            MESSAGE_WHEN_FIELD.setAccessible(true);
-            MESSAGE_MARK_IN_USE_METHOD = Message.class.getDeclaredMethod("markInUse");
-            MESSAGE_MARK_IN_USE_METHOD.setAccessible(true);
+
+            if (isAtLeastBaklava()) {
+                MESSAGE_QUEUE_MESSAGES_FIELD = null;
+                MESSAGE_NEXT_FIELD = null;
+                MESSAGE_WHEN_FIELD = null;
+                MESSAGE_MARK_IN_USE_METHOD = null;
+            } else {
+                MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
+                MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
+                MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
+                MESSAGE_NEXT_FIELD.setAccessible(true);
+                MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
+                MESSAGE_WHEN_FIELD.setAccessible(true);
+                MESSAGE_MARK_IN_USE_METHOD = Message.class.getDeclaredMethod("markInUse");
+                MESSAGE_MARK_IN_USE_METHOD.setAccessible(true);
+            }
         } catch (NoSuchFieldException | NoSuchMethodException e) {
             throw new RuntimeException("Failed to initialize TestLooper", e);
         }
@@ -100,6 +127,13 @@
             throw new RuntimeException("Reflection error constructing or accessing looper", e);
         }
 
+        if (isAtLeastBaklava()) {
+            mTestLooperManager =
+                InstrumentationRegistry.getInstrumentation().acquireLooperManager(mLooper);
+        } else {
+            mTestLooperManager = null;
+        }
+
         mClock = clock;
     }
 
@@ -111,19 +145,72 @@
         return new HandlerExecutor(new Handler(getLooper()));
     }
 
-    private Message getMessageLinkedList() {
+    private Message getMessageLinkedListLegacy() {
         try {
             MessageQueue queue = mLooper.getQueue();
             return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
         } catch (IllegalAccessException e) {
             throw new RuntimeException("Access failed in TestLooper: get - MessageQueue.mMessages",
-                    e);
+                e);
         }
     }
 
     public void moveTimeForward(long milliSeconds) {
+        if (isAtLeastBaklava()) {
+            moveTimeForwardBaklava(milliSeconds);
+        } else {
+            moveTimeForwardLegacy(milliSeconds);
+        }
+    }
+
+    private void moveTimeForwardBaklava(long milliSeconds) {
+        // Drain all Messages from the queue.
+        Queue<Message> messages = new ArrayDeque<>();
+        while (true) {
+            Message message = mTestLooperManager.poll();
+            if (message == null) {
+                break;
+            }
+
+            // Adjust the Message's delivery time.
+            long newWhen = message.when - milliSeconds;
+            if (newWhen < 0) {
+                newWhen = 0;
+            }
+            message.when = newWhen;
+            messages.add(message);
+        }
+
+        // Repost all Messages back to the queuewith a new time.
+        while (true) {
+            Message message = messages.poll();
+            if (message == null) {
+                break;
+            }
+
+            Runnable callback = message.getCallback();
+            Handler handler = message.getTarget();
+            long when = message.getWhen();
+
+            // The Message cannot be re-enqueued because it is marked in use.
+            // Make a copy of the Message and recycle the original.
+            // This resets {@link Message#isInUse()} but retains all other content.
+            {
+                Message newMessage = Message.obtain();
+                newMessage.copyFrom(message);
+                newMessage.setCallback(callback);
+                mTestLooperManager.recycle(message);
+                message = newMessage;
+            }
+
+            // Send the Message back to its Handler to be re-enqueued.
+            handler.sendMessageAtTime(message, when);
+        }
+    }
+
+    private void moveTimeForwardLegacy(long milliSeconds) {
         try {
-            Message msg = getMessageLinkedList();
+            Message msg = getMessageLinkedListLegacy();
             while (msg != null) {
                 long updatedWhen = msg.getWhen() - milliSeconds;
                 if (updatedWhen < 0) {
@@ -141,12 +228,12 @@
         return mClock.uptimeMillis();
     }
 
-    private Message messageQueueNext() {
+    private Message messageQueueNextLegacy() {
         try {
             long now = currentTime();
 
             Message prevMsg = null;
-            Message msg = getMessageLinkedList();
+            Message msg = getMessageLinkedListLegacy();
             if (msg != null && msg.getTarget() == null) {
                 // Stalled by a barrier. Find the next asynchronous message in
                 // the queue.
@@ -179,18 +266,46 @@
     /**
      * @return true if there are pending messages in the message queue
      */
-    public synchronized boolean isIdle() {
-        Message messageList = getMessageLinkedList();
+    public boolean isIdle() {
+        if (isAtLeastBaklava()) {
+            return isIdleBaklava();
+        } else {
+            return isIdleLegacy();
+        }
+    }
 
+    private boolean isIdleBaklava() {
+        Long when = mTestLooperManager.peekWhen();
+        return when != null && currentTime() >= when;
+    }
+
+    private synchronized boolean isIdleLegacy() {
+        Message messageList = getMessageLinkedListLegacy();
         return messageList != null && currentTime() >= messageList.getWhen();
     }
 
     /**
      * @return the next message in the Looper's message queue or null if there is none
      */
-    public synchronized Message nextMessage() {
+    public Message nextMessage() {
+        if (isAtLeastBaklava()) {
+            return nextMessageBaklava();
+        } else {
+            return nextMessageLegacy();
+        }
+    }
+
+    private Message nextMessageBaklava() {
         if (isIdle()) {
-            return messageQueueNext();
+            return mTestLooperManager.poll();
+        } else {
+            return null;
+        }
+    }
+
+    private synchronized Message nextMessageLegacy() {
+        if (isIdle()) {
+            return messageQueueNextLegacy();
         } else {
             return null;
         }
@@ -200,9 +315,26 @@
      * Dispatch the next message in the queue
      * Asserts that there is a message in the queue
      */
-    public synchronized void dispatchNext() {
+    public void dispatchNext() {
+        if (isAtLeastBaklava()) {
+            dispatchNextBaklava();
+        } else {
+            dispatchNextLegacy();
+        }
+    }
+
+    private void dispatchNextBaklava() {
         assertTrue(isIdle());
-        Message msg = messageQueueNext();
+        Message msg = mTestLooperManager.poll();
+        if (msg == null) {
+            return;
+        }
+        msg.getTarget().dispatchMessage(msg);
+    }
+
+    private synchronized void dispatchNextLegacy() {
+        assertTrue(isIdle());
+        Message msg = messageQueueNextLegacy();
         if (msg == null) {
             return;
         }
diff --git a/tools/aapt2/tools/finalize_res.py b/tools/aapt2/tools/finalize_res.py
index f9add7d..059f3b2 100755
--- a/tools/aapt2/tools/finalize_res.py
+++ b/tools/aapt2/tools/finalize_res.py
@@ -45,12 +45,14 @@
             "anim", "animator", "interpolator", "mipmap", "integer", "transition", "raw", "bool",
             "fraction"]
 
+NO_FLAG_MAGIC_CONSTANT = "no_flag"
+
 _aconfig_map = {}
 _not_finalized = defaultdict(list)
 _type_ids = {}
 _type = ""
-_finalized_flags = set()
-_non_finalized_flags = set()
+_finalized_flags = defaultdict(list)
+_non_finalized_flags = defaultdict(list)
 
 
 _lowest_staging_first_id = 0x01FFFFFF
@@ -71,7 +73,12 @@
 
     comment = re.search(' *<!--.+?-->\n', comment_and_item, flags=re.DOTALL).group(0)
 
-    flag = re.search('<!-- @FlaggedApi\((.+?)\)', comment, flags=re.DOTALL).group(1)
+    match = re.search('<!-- @FlaggedApi\((.+?)\)', comment, flags=re.DOTALL)
+    if match:
+        flag = match.group(1)
+    else:
+        flag = NO_FLAG_MAGIC_CONSTANT
+
     if flag.startswith("\""):
         # Flag is a string value, just remove "
         flag = flag.replace("\"", "")
@@ -84,13 +91,13 @@
 
     # READ_ONLY-ENABLED is a magic string from printflags output below
     if _aconfig_map[flag] != "READ_ONLY-ENABLED":
-        _non_finalized_flags.add(flag)
+        _non_finalized_flags[flag].append(name)
         # Keep it as is in <staging-public-group> in public-staging.xml
         # Include as magic constant "removed_" in <staging-public-group-final> in public-final.xml
         # Don't assign an id in public-final.xml
         return (comment_and_item, "    <public name=\"removed_\" />\n", "")
 
-    _finalized_flags.add(flag)
+    _finalized_flags[flag].append(name)
 
     id = _type_ids[_type]
     _type_ids[_type] += 1
@@ -154,6 +161,8 @@
     value = parts[1]
     _aconfig_map[key]=value
 
+_aconfig_map[NO_FLAG_MAGIC_CONSTANT]="READ_ONLY-DISABLED"
+
 with open(sys.argv[1], "r+") as stagingFile:
     with open(sys.argv[2], "r+") as finalFile:
         existing = finalFile.read()
@@ -209,9 +218,13 @@
 
 
 print("\nFlags that had resources that were NOT finalized:")
-for flag in sorted(_non_finalized_flags):
-    print(flag)
+for flag in sorted(_non_finalized_flags.keys()):
+    print(f"  {flag}")
+    for value in _non_finalized_flags[flag]:
+        print(f"    {value}")
 
 print("\nFlags that had resources that were finalized:")
-for flag in sorted(_finalized_flags):
-    print(flag)
+for flag in sorted(_finalized_flags.keys()):
+    print(f"  {flag}")
+    for value in _finalized_flags[flag]:
+        print(f"    {value}")